/** * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @author Arne Kepp, Marius Suta, The Open Planning Project, Copyright 2008 - 2015 */ package org.geowebcache.config; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.geowebcache.GeoWebCacheException; import org.geowebcache.GeoWebCacheExtensions; import org.geowebcache.config.ContextualConfigurationProvider.Context; import org.geowebcache.config.legends.LegendsRawInfo; import org.geowebcache.config.legends.LegendsRawInfoConverter; import org.geowebcache.config.meta.ServiceInformation; import org.geowebcache.filter.parameters.CaseNormalizer; import org.geowebcache.filter.parameters.FloatParameterFilter; import org.geowebcache.filter.parameters.IntegerParameterFilter; import org.geowebcache.filter.parameters.ParameterFilter; import org.geowebcache.filter.parameters.RegexParameterFilter; import org.geowebcache.filter.parameters.StringParameterFilter; import org.geowebcache.filter.request.CircularExtentFilter; import org.geowebcache.filter.request.FileRasterFilter; import org.geowebcache.filter.request.WMSRasterFilter; import org.geowebcache.grid.GridSet; import org.geowebcache.grid.GridSetBroker; import org.geowebcache.io.GeoWebCacheXStream; import org.geowebcache.layer.ExpirationRule; import org.geowebcache.layer.TileLayer; import org.geowebcache.layer.meta.ContactInformation; import org.geowebcache.layer.meta.LayerMetaInformation; import org.geowebcache.layer.updatesource.GeoRSSFeedDefinition; import org.geowebcache.layer.wms.WMSHttpHelper; import org.geowebcache.layer.wms.WMSLayer; import org.geowebcache.locks.LockProvider; import org.geowebcache.mime.FormatModifier; import org.geowebcache.seed.SeedRequest; import org.geowebcache.seed.TruncateLayerRequest; import org.geowebcache.storage.DefaultStorageFinder; import org.geowebcache.util.ApplicationContextProvider; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomReader; /** * XMLConfiguration class responsible for reading/writing layer configurations to and from XML file * <p> * NOTE {@link #initialize(GridSetBroker)} MUST have been called before any other method is used, * otherwise this configuration is in an inconsistent and unpredictable state. * </p> */ public class XMLConfiguration implements Configuration, InitializingBean { public static final String DEFAULT_CONFIGURATION_FILE_NAME = "geowebcache.xml"; private static Log log = LogFactory.getLog(org.geowebcache.config.XMLConfiguration.class); /** * Web app context, used to look up {@link XMLConfigurationProvider}s. Will be null if used the * {@link #XMLConfiguration(File)} constructor */ private final WebApplicationContext context; private final ConfigurationResourceProvider resourceProvider; private GeoWebCacheConfiguration gwcConfig; private transient Map<String, TileLayer> layers; private GridSetBroker gridSetBroker; /** * A flag for whether the config needs to be loaded at {@link #initialize(GridSetBroker)}. If * the constructor loads the configuration, will set it to false, then each call to initialize() * will reset this flag to true */ private boolean reloadConfigOnInit = true; /** * Base Constructor with custom ConfiguratioNResourceProvider * * @param appCtx use to lookup {@link XMLConfigurationProvider} extensions, may be {@code null} * @param inFac */ public XMLConfiguration(final ApplicationContextProvider appCtx, final ConfigurationResourceProvider inFac) { this.context = appCtx == null ? null : appCtx.getApplicationContext(); this.resourceProvider = inFac; } /** * File System based Constructor * * @param appCtx use to lookup {@link XMLConfigurationProvider} extensions, may be {@code null} * @param configFileDirectory * @param storageDirFinder * @throws ConfigurationException */ public XMLConfiguration(final ApplicationContextProvider appCtx, final String configFileDirectory, final DefaultStorageFinder storageDirFinder) throws ConfigurationException { this(appCtx, new XMLFileResourceProvider(DEFAULT_CONFIGURATION_FILE_NAME, appCtx, configFileDirectory, storageDirFinder)); resourceProvider.setTemplate("/" + DEFAULT_CONFIGURATION_FILE_NAME); } /** * Constructor that will look for {@code geowebcache.xml} at the directory defined by * {@code storageDirFinder} * * @param appCtx * use to lookup {@link XMLConfigurationProvider} extenions, may be {@code null} * @param defaultStorage * @throws ConfigurationException */ public XMLConfiguration(final ApplicationContextProvider appCtx, final DefaultStorageFinder storageDirFinder) throws ConfigurationException { this(appCtx, new XMLFileResourceProvider(DEFAULT_CONFIGURATION_FILE_NAME, appCtx, storageDirFinder)); resourceProvider.setTemplate("/" + DEFAULT_CONFIGURATION_FILE_NAME); } /** * Constructor that will accept an absolute or relative path for finding {@code geowebcache.xml} * * @param appCtx * @param configFileDirectory * @throws ConfigurationException */ public XMLConfiguration(final ApplicationContextProvider appCtx, final String configFileDirectory) throws ConfigurationException { this(appCtx, configFileDirectory, null); } /** * @deprecated use {@link #XMLFileConfiguration(ApplicationContextProvider, DefaultStorageFinder)} */ @Deprecated public XMLConfiguration(final ApplicationContextProvider appCtx, final GridSetBroker gridSetBroker, final DefaultStorageFinder storageDirFinder) throws ConfigurationException { this(appCtx, storageDirFinder); log.warn("This constructor is deprecated"); } /** * @deprecated use {@link #XMLFileConfiguration(ApplicationContextProvider, String)} */ @Deprecated public XMLConfiguration(final ApplicationContextProvider appCtx, final GridSetBroker gridSetBroker, final String configFileDirectory) throws ConfigurationException { this(appCtx, configFileDirectory); log.warn("This constructor is deprecated"); } @Override public void afterPropertiesSet() throws Exception { if (resourceProvider.hasInput()) { this.gwcConfig = loadConfiguration(); } this.reloadConfigOnInit = false; } /** * Constructor with inputstream (only for testing) * @throws ConfigurationException */ public XMLConfiguration(final InputStream is) throws ConfigurationException { this (null, new ConfigurationResourceProvider() { @Override public InputStream in() { throw new UnsupportedOperationException(); } @Override public OutputStream out() throws IOException { throw new UnsupportedOperationException(); } @Override public void backup() throws IOException { throw new UnsupportedOperationException(); } @Override public void setTemplate(String template) { throw new UnsupportedOperationException(); } @Override public String getLocation() throws IOException { throw new UnsupportedOperationException(); } @Override public String getId() { return "mockConfig"; } @Override public boolean hasInput() { return false; } @Override public boolean hasOutput() { return false; } }); try { gwcConfig = loadConfiguration(is); } catch (IOException e) { throw new ConfigurationException(e.getMessage(), e); } } public void setTemplate(String template) { resourceProvider.setTemplate(template); } public String getConfigLocation() throws ConfigurationException { try { return resourceProvider.getLocation(); } catch (IOException e) { throw new ConfigurationException(e.getMessage(), e); } } public boolean isRuntimeStatsEnabled() { if (gwcConfig == null || gwcConfig.getRuntimeStats() == null) { return true; } else { return gwcConfig.getRuntimeStats(); } } public synchronized ServiceInformation getServiceInformation() { return gwcConfig.getServiceInformation(); } /** * Configuration objects lacking their own defaults can delegate to this * @param layer */ public void setDefaultValues(TileLayer layer) { // Additional values that can have defaults set if (layer.isCacheBypassAllowed() == null) { if (gwcConfig.getCacheBypassAllowed() != null) { layer.setCacheBypassAllowed(gwcConfig.getCacheBypassAllowed()); } else { layer.setCacheBypassAllowed(false); } } if (layer.getBackendTimeout() == null) { if (gwcConfig.getBackendTimeout() != null) { layer.setBackendTimeout(gwcConfig.getBackendTimeout()); } else { layer.setBackendTimeout(120); } } if (layer.getFormatModifiers() == null) { if (gwcConfig.getFormatModifiers() != null) { layer.setFormatModifiers(gwcConfig.getFormatModifiers()); } } if (layer instanceof WMSLayer) { WMSLayer wl = (WMSLayer) layer; URL proxyUrl = null; try { if (gwcConfig.getProxyUrl() != null) { proxyUrl = new URL(gwcConfig.getProxyUrl()); log.debug("Using proxy " + proxyUrl.getHost() + ":" + proxyUrl.getPort()); } else if (wl.getProxyUrl() != null) { proxyUrl = new URL(wl.getProxyUrl()); log.debug("Using proxy " + proxyUrl.getHost() + ":" + proxyUrl.getPort()); } } catch (MalformedURLException e) { log.error("could not parse proxy URL " + wl.getProxyUrl() + " ! continuing WITHOUT proxy!", e); } final WMSHttpHelper sourceHelper; if (wl.getHttpUsername() != null) { sourceHelper = new WMSHttpHelper(wl.getHttpUsername(), wl.getHttpPassword(), proxyUrl); log.debug("Using per-layer HTTP credentials for " + wl.getName() + ", " + "username " + wl.getHttpUsername()); } else if (gwcConfig.getHttpUsername() != null) { sourceHelper = new WMSHttpHelper(gwcConfig.getHttpUsername(), gwcConfig.getHttpPassword(), proxyUrl); log.debug("Using global HTTP credentials for " + wl.getName()); } else { sourceHelper = new WMSHttpHelper(null, null, proxyUrl); log.debug("Not using HTTP credentials for " + wl.getName()); } wl.setSourceHelper(sourceHelper); wl.setLockProvider(gwcConfig.getLockProvider()); } } private GeoWebCacheConfiguration loadConfiguration() throws ConfigurationException { Assert.isTrue(resourceProvider.hasInput()); InputStream in; try { in = resourceProvider.in(); try { return loadConfiguration(in); } finally { in.close(); } } catch (IOException e) { throw new ConfigurationException("Error parsing config file " + resourceProvider.getId(), e); } } private GeoWebCacheConfiguration loadConfiguration(InputStream xmlFile) throws IOException, ConfigurationException { Node rootNode = loadDocument(xmlFile); XStream xs = getConfiguredXStreamWithContext(new GeoWebCacheXStream(), Context.PERSIST); GeoWebCacheConfiguration config; config = (GeoWebCacheConfiguration) xs.unmarshal(new DomReader((Element) rootNode)); return config; } /** * @see org.geowebcache.config.Configuration#save() */ public synchronized void save() throws IOException { if (!resourceProvider.hasOutput()) { return; } try { resourceProvider.backup(); } catch (Exception e) { log.warn("Error creating back up of configuration file " + resourceProvider.getId(), e); } persistToFile(); } public XStream getConfiguredXStream(XStream xs) { return getConfiguredXStreamWithContext(xs, this.context, (ContextualConfigurationProvider.Context)null); } public static XStream getConfiguredXStream(XStream xs, WebApplicationContext context) { return getConfiguredXStreamWithContext(xs, context, (ContextualConfigurationProvider.Context)null); } public XStream getConfiguredXStreamWithContext(XStream xs, ContextualConfigurationProvider.Context providerContext) { return getConfiguredXStreamWithContext(xs, this.context, providerContext); } public static XStream getConfiguredXStreamWithContext(XStream xs, WebApplicationContext context, ContextualConfigurationProvider.Context providerContext) { { // Allow any implementation of these extension points xs.allowTypeHierarchy(org.geowebcache.layer.TileLayer.class); xs.allowTypeHierarchy(org.geowebcache.filter.parameters.ParameterFilter.class); xs.allowTypeHierarchy(org.geowebcache.filter.request.RequestFilter.class); xs.allowTypeHierarchy(org.geowebcache.config.BlobStoreConfig.class); xs.allowTypeHierarchy(org.geowebcache.config.Configuration.class); // Allow anything that's part of GWC // TODO: replace this with a more narrow whitelist xs.allowTypesByWildcard(new String[]{"org.geowebcache.**"}); } xs.setMode(XStream.NO_REFERENCES); xs.addDefaultImplementation(ArrayList.class, List.class); xs.alias("gwcConfiguration", GeoWebCacheConfiguration.class); xs.useAttributeFor(GeoWebCacheConfiguration.class, "xmlns_xsi"); xs.aliasField("xmlns:xsi", GeoWebCacheConfiguration.class, "xmlns_xsi"); xs.useAttributeFor(GeoWebCacheConfiguration.class, "xsi_schemaLocation"); xs.aliasField("xsi:schemaLocation", GeoWebCacheConfiguration.class, "xsi_schemaLocation"); xs.useAttributeFor(GeoWebCacheConfiguration.class, "xmlns"); // xs.alias("layers", List.class); xs.alias("wmsLayer", WMSLayer.class); // configuration for legends info xs.registerConverter(new LegendsRawInfoConverter()); xs.alias("legends", LegendsRawInfo.class); xs.alias("blobStores", new ArrayList<BlobStoreConfig>().getClass()); xs.alias("FileBlobStore", FileBlobStoreConfig.class); xs.aliasAttribute(BlobStoreConfig.class, "_default", "default"); // These two are for 1.1.x compatibility xs.alias("grids", new ArrayList<XMLOldGrid>().getClass()); xs.alias("grid", XMLOldGrid.class); xs.alias("gridSet", XMLGridSet.class); xs.alias("gridSubset", XMLGridSubset.class); xs.alias("mimeFormats", new ArrayList<String>().getClass()); xs.alias("formatModifiers", new ArrayList<FormatModifier>().getClass()); xs.alias("srs", org.geowebcache.grid.SRS.class); xs.alias("parameterFilters", new ArrayList<ParameterFilter>().getClass()); xs.alias("parameterFilter", ParameterFilter.class); xs.alias("seedRequest", SeedRequest.class); xs.processAnnotations(CaseNormalizer.class); xs.processAnnotations(StringParameterFilter.class); xs.processAnnotations(RegexParameterFilter.class); xs.processAnnotations(FloatParameterFilter.class); xs.processAnnotations(IntegerParameterFilter.class); xs.alias("formatModifier", FormatModifier.class); xs.alias("circularExtentFilter", CircularExtentFilter.class); xs.alias("wmsRasterFilter", WMSRasterFilter.class); xs.alias("fileRasterFilter", FileRasterFilter.class); xs.alias("expirationRule", ExpirationRule.class); xs.useAttributeFor(ExpirationRule.class, "minZoom"); xs.useAttributeFor(ExpirationRule.class, "expiration"); xs.alias("geoRssFeed", GeoRSSFeedDefinition.class); xs.alias("metaInformation", LayerMetaInformation.class); xs.alias("serviceInformation", ServiceInformation.class); xs.alias("contactInformation", ContactInformation.class); xs.processAnnotations(TruncateLayerRequest.class); if (context != null) { /* * Look up XMLConfigurationProvider extension points and let them contribute to the * configuration */ List<XMLConfigurationProvider> configExtensions = GeoWebCacheExtensions.extensions( XMLConfigurationProvider.class, context); for (XMLConfigurationProvider extension : configExtensions) { // Check if the provider is context dependent if(extension instanceof ContextualConfigurationProvider && // Check if the context is applicable for the provider (providerContext==null || !((ContextualConfigurationProvider)extension).appliesTo(providerContext))) { // If so, try the next one continue; } xs = extension.getConfiguredXStream(xs); } } return xs; } /** * Method responsible for writing out the entire GeoWebCacheConfiguration object * * throws an exception if it does not succeed */ private void persistToFile() throws IOException { Assert.isTrue(resourceProvider.hasOutput()); // create the XStream for serializing the configuration XStream xs = getConfiguredXStreamWithContext(new GeoWebCacheXStream(), Context.PERSIST); try (OutputStreamWriter writer = new OutputStreamWriter(resourceProvider.out(), "UTF-8")) { // set version to latest String currentSchemaVersion = getCurrentSchemaVersion(); gwcConfig.setVersion(currentSchemaVersion); writer.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); xs.toXML(gwcConfig, writer); } catch (UnsupportedEncodingException uee) { uee.printStackTrace(); throw new IOException(uee.getMessage()); } catch (FileNotFoundException fnfe) { throw fnfe; } catch (IOException e) { throw (IOException) new IOException("Error writing to " + resourceProvider.getId() + ": " + e.getMessage()).initCause(e); } log.info("Wrote configuration to " + resourceProvider.getId()); } /** * @return {@code true} only if {@code tl instanceof WMSLayer} * @see org.geowebcache.config.Configuration#canSave(org.geowebcache.layer.TileLayer) */ public boolean canSave(TileLayer tl) { return tl instanceof WMSLayer && !tl.isTransientLayer(); } /** * @param tl * the layer to add to this configuration * @return * @throws IllegalArgumentException * if a layer named the same than {@code tl} already exists * @see org.geowebcache.config.Configuration#addLayer(org.geowebcache.layer.TileLayer) */ public synchronized void addLayer(TileLayer tl) throws IllegalArgumentException { if (tl == null) { throw new NullPointerException(); } if (!(tl instanceof WMSLayer)) { throw new IllegalArgumentException("Can't add layers of type " + tl.getClass().getName()); } if (null != getTileLayer(tl.getName())) { throw new IllegalArgumentException("Layer '" + tl.getName() + "' already exists"); } initialize(tl); gwcConfig.getLayers().add(tl); updateLayers(); } /** * Method responsible for modifying an existing layer. * * @param tl * the new layer to overwrite the existing layer * @throws NoSuchElementException * @see org.geowebcache.config.Configuration#modifyLayer(org.geowebcache.layer.TileLayer) */ public synchronized void modifyLayer(TileLayer tl) throws NoSuchElementException { TileLayer previous = getTileLayer(tl.getName()); if (null == previous) { throw new NoSuchElementException("Layer " + tl.getName() + " does not exist"); } gwcConfig.getLayers().remove(previous); initialize(tl); gwcConfig.getLayers().add(tl); updateLayers(); } /** * @return {@code true} if the layer was removed, {@code false} if no such layer exists * @see org.geowebcache.config.Configuration#removeLayer(java.lang.String) */ public synchronized boolean removeLayer(final String layerName) { final TileLayer tileLayer = getTileLayer(layerName); if (tileLayer == null) { return false; } boolean removed = false; removed = gwcConfig.getLayers().remove(tileLayer); if (removed) { updateLayers(); } return removed; } /** * @param gridSet * @throws GeoWebCacheException */ public synchronized void addOrReplaceGridSet(final XMLGridSet gridSet) throws IllegalArgumentException { final String gridsetName = gridSet.getName(); List<XMLGridSet> gridSets = gwcConfig.getGridSets(); for (Iterator<XMLGridSet> it = gridSets.iterator(); it.hasNext();) { XMLGridSet gset = it.next(); if (gridsetName.equals(gset.getName())) { it.remove(); } } gridSets.add(gridSet); } /** * Removes and returns the gridset configuration named {@code gridsetName}. * * @param gridsetName * the name of the gridset to remove * @return the removed griset, or {@code null} if no such gridset exists */ public synchronized XMLGridSet removeGridset(final String gridsetName) { List<XMLGridSet> gridSets = gwcConfig.getGridSets(); for (Iterator<XMLGridSet> it = gridSets.iterator(); it.hasNext();) { XMLGridSet gset = it.next(); if (gridsetName.equals(gset.getName())) { it.remove(); return gset; } } return null; } /** * Method responsible for loading xml configuration file and parsing it into a W3C DOM Document * * @param file * the file contaning the layer configurations * @return W3C DOM Document */ static Node loadDocument(InputStream xmlFile) throws ConfigurationException, IOException { Node topNode = null; try { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); docBuilderFactory.setNamespaceAware(true); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); topNode = checkAndTransform(docBuilder.parse(xmlFile)); } catch (Exception e) { throw (IOException) new IOException(e.getMessage()).initCause(e); } return topNode; } private static Node checkAndTransform(Document doc) throws ConfigurationException { Node rootNode = doc.getDocumentElement(); // debugPrint(rootNode); if (!rootNode.getNodeName().equals("gwcConfiguration")) { log.info("The configuration file is of the pre 1.0 type, trying to convert."); rootNode = applyTransform(rootNode, "geowebcache_pre10.xsl").getFirstChild(); } // debugPrint(rootNode); if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.0.0")) { log.info("Updating configuration from 1.0.0 to 1.0.1"); rootNode = applyTransform(rootNode, "geowebcache_100.xsl").getFirstChild(); } // debugPrint(rootNode); if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.0.1")) { log.info("Updating configuration from 1.0.1 to 1.0.2"); rootNode = applyTransform(rootNode, "geowebcache_101.xsl").getFirstChild(); } // debugPrint(rootNode); if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.0.2")) { log.info("Updating configuration from 1.0.2 to 1.1.0"); rootNode = applyTransform(rootNode, "geowebcache_102.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.1.0")) { log.info("Updating configuration from 1.1.0 to 1.1.3"); rootNode = applyTransform(rootNode, "geowebcache_110.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.1.3")) { log.info("Updating configuration from 1.1.3 to 1.1.4"); rootNode = applyTransform(rootNode, "geowebcache_113.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.1.4")) { log.info("Updating configuration from 1.1.4 to 1.1.5"); rootNode = applyTransform(rootNode, "geowebcache_114.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.1.5")) { log.info("Updating configuration from 1.1.5 to 1.2.0"); rootNode = applyTransform(rootNode, "geowebcache_115.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.2.0")) { log.info("Updating configuration from 1.2.0 to 1.2.1"); rootNode = applyTransform(rootNode, "geowebcache_120.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.2.1")) { log.info("Updating configuration from 1.2.1 to 1.2.2"); rootNode = applyTransform(rootNode, "geowebcache_121.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.2.2")) { log.info("Updating configuration from 1.2.2 to 1.2.4"); rootNode = applyTransform(rootNode, "geowebcache_122.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.2.4")) { log.info("Updating configuration from 1.2.4 to 1.2.5"); rootNode = applyTransform(rootNode, "geowebcache_124.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.2.5")) { log.info("Updating configuration from 1.2.5 to 1.2.6"); rootNode = applyTransform(rootNode, "geowebcache_125.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.2.6")) { log.info("Updating configuration from 1.2.6 to 1.5.0"); rootNode = applyTransform(rootNode, "geowebcache_126.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.5.0")) { log.info("Updating configuration from 1.5.0 to 1.5.1"); rootNode = applyTransform(rootNode, "geowebcache_150.xsl").getFirstChild(); } if (rootNode.getNamespaceURI().equals("http://geowebcache.org/schema/1.5.1")) { log.info("Updating configuration from 1.5.1 to 1.6.0"); rootNode = applyTransform(rootNode, "geowebcache_151.xsl").getFirstChild(); } // Check again after transform if (!rootNode.getNodeName().equals("gwcConfiguration")) { log.error("Unable to parse file, expected gwcConfiguration at root after transform."); throw new ConfigurationException("Unable to parse after transform."); } else { // Parsing the schema file try { validate(rootNode); log.info("Configuration file validated fine."); } catch (SAXException e) { String msg = "*** GWC configuration validation error: " + e.getMessage(); char[] c = new char[4 + msg.length()]; Arrays.fill(c, '*'); String warndecoration = new String(c).substring(0, 80); log.warn(warndecoration); log.warn(msg); log.warn("*** Will try to use configuration anyway. Please check the order of declared elements against the schema."); log.warn(warndecoration); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } } return rootNode; } static void validate(Node rootNode) throws SAXException, IOException { // Perform validation // TODO dont know why this one suddenly failed to look up, revert to // XMLConstants.W3C_XML_SCHEMA_NS_URI SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); InputStream is = XMLConfiguration.class.getResourceAsStream("geowebcache.xsd"); Schema schema = factory.newSchema(new StreamSource(is)); Validator validator = schema.newValidator(); // debugPrint(rootNode); DOMSource domSrc = new DOMSource(rootNode); validator.validate(domSrc); } static String getCurrentSchemaVersion() { InputStream is = XMLConfiguration.class.getResourceAsStream("geowebcache.xsd"); Document dom; try { dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is); } catch (Exception e) { throw new RuntimeException(e); } finally { try { is.close(); } catch (IOException e) { throw new RuntimeException(e); } } String version = dom.getDocumentElement().getAttribute("version"); if (null == version || version.trim().length() == 0) { throw new IllegalStateException("Schema doesn't define version"); } return version.trim(); } private static Node applyTransform(Node oldRootNode, String xslFilename) { DOMResult result = new DOMResult(); Transformer transformer; InputStream is = XMLConfiguration.class.getResourceAsStream(xslFilename); try { transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(is)); transformer.transform(new DOMSource(oldRootNode), result); } catch (TransformerConfigurationException e) { e.printStackTrace(); } catch (TransformerFactoryConfigurationError e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } return result.getNode(); } /** * @see org.geowebcache.config.Configuration#initialize(org.geowebcache.grid.GridSetBroker) */ public int initialize(final GridSetBroker gridSetBroker) throws GeoWebCacheException { this.gridSetBroker = gridSetBroker; if (this.reloadConfigOnInit && resourceProvider.hasInput()) { this.gwcConfig = loadConfiguration(); } log.info("Initializing GridSets from " + getIdentifier()); contributeGridSets(gridSetBroker); log.info("Initializing layers from " + getIdentifier()); // Loop over the layers and set appropriate values for (TileLayer layer : gwcConfig.getLayers()) { if (layer == null) { throw new IllegalStateException(getIdentifier() + " contains a null layer"); } initialize(layer); } updateLayers(); this.reloadConfigOnInit = true; return getTileLayerCount(); } private void updateLayers() { Map<String, TileLayer> buff = new HashMap<String, TileLayer>(); for (TileLayer layer : gwcConfig.getLayers()) { buff.put(layer.getName(), layer); } this.layers = buff; } private void contributeGridSets(final GridSetBroker gridSetBroker) { if (gwcConfig.getGridSets() != null) { Iterator<XMLGridSet> iter = gwcConfig.getGridSets().iterator(); while (iter.hasNext()) { XMLGridSet xmlGridSet = iter.next(); if (log.isDebugEnabled()) { log.debug("Reading " + xmlGridSet.getName()); } GridSet gridSet = xmlGridSet.makeGridSet(); log.info("Read GridSet " + gridSet.getName()); gridSetBroker.put(gridSet); } } } private void initialize(final TileLayer layer) { log.info("Initializing TileLayer '" + layer.getName() + "'"); setDefaultValues(layer); layer.initialize(gridSetBroker); } /** * @see org.geowebcache.config.Configuration#getIdentifier() */ public String getIdentifier() { return resourceProvider.getId(); } public void setRelativePath(String relPath) { log.error("Specifying the relative path as a property is deprecated. " + "Please pass it as the 4th argument to the constructor."); } public void setAbsolutePath(String absPath) { log.error("Specifying the absolute path as a property is deprecated. " + "Please pass it as the 4th argument to the constructor."); } /** * @see org.geowebcache.config.Configuration#getTileLayers() */ public List<TileLayer> getTileLayers() { return Collections.unmodifiableList(gwcConfig.getLayers()); } /** * @see org.geowebcache.config.Configuration#getLayers() */ public Iterable<TileLayer> getLayers() { return Collections.unmodifiableList(gwcConfig.getLayers()); } /** * @see org.geowebcache.config.Configuration#getTileLayer(java.lang.String) */ public TileLayer getTileLayer(String layerName) { return layers.get(layerName); } /** * @see org.geowebcache.config.Configuration#getTileLayerById(String) */ public TileLayer getTileLayerById(String layerId) { // this configuration does not differentiate between identifier and identity yet return layers.get(layerId); } /** * @see org.geowebcache.config.Configuration#containsLayer(java.lang.String) */ public boolean containsLayer(String layerId) { return layers.containsKey(layerId); } /** * @see org.geowebcache.config.Configuration#getTileLayerCount() */ public int getTileLayerCount() { return layers.size(); } /** * @see org.geowebcache.config.Configuration#getTileLayerNames() */ public Set<String> getTileLayerNames() { Set<String> names = Collections.unmodifiableSet(this.layers.keySet()); return names; } public String getVersion() { return gwcConfig.getVersion(); } /** * Used for getting the "fullWMS" parameter from GeoWebCacheConfigration * @return */ public Boolean getfullWMS(){ if(gwcConfig!=null){ return gwcConfig.getFullWMS(); } return null; } public List<BlobStoreConfig> getBlobStores() { return gwcConfig.getBlobStores(); } public LockProvider getLockProvider() { return gwcConfig.getLockProvider(); } }