/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.gwc.config;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.geoserver.gwc.GWC.tileLayerName;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.MetadataMap;
import org.geoserver.catalog.PublishedInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.config.GeoServerInitializer;
import org.geoserver.gwc.ConfigurableBlobStore;
import org.geoserver.gwc.layer.CatalogConfiguration;
import org.geoserver.gwc.layer.GeoServerTileLayerInfo;
import org.geoserver.gwc.layer.GeoServerTileLayerInfoImpl;
import org.geoserver.gwc.layer.LegacyTileLayerInfoLoader;
import org.geoserver.gwc.layer.TileLayerCatalog;
import org.geoserver.gwc.layer.TileLayerInfoUtil;
import org.geoserver.gwc.wmts.WMTSInfo;
import org.geoserver.platform.resource.Resource;
import org.geoserver.platform.resource.Resource.Type;
import org.geoserver.wms.WMSInfo;
import org.geotools.util.Version;
import org.geotools.util.logging.Logging;
import org.geowebcache.storage.blobstore.memory.CacheConfiguration;
import org.geowebcache.storage.blobstore.memory.CacheProvider;
import org.geowebcache.storage.blobstore.memory.guava.GuavaCacheProvider;
import org.opengis.filter.Filter;
/**
* GeoSever initialization hook that preserves backwards compatible GWC configuration at start up.
* <p>
* For instance, this initializer:
* <ul>
* <i> Creates a <data directory>/gwc-gs.xml configuration file if it doesn't exist, populated with
* old defaults global configuration for gwc layers based on geoserver layers and layer groups.
* <li>Configures a gwc {@link GeoServerTileLayerInfoImpl tile layer} for every {@link LayerInfo}
* and {@link LayerGroupInfo} matching the old defaults, also only if {@code gwc-gs.xml} didn't
* already exist.
* <li>Upgrades the direct WMS integration configuration from an old config. Before using
* {@code gwc-gs.xml} to hold the integrated GWC configuration, the only property configured was
* whether the direct WMS integration option was enabled, and it was saved as part of the
* {@link WMSInfo} metadata map under the {@code GWC_WMS_Integration} key. This method removes that
* key from WMSInfo if present and sets its value to the {@code GWCConfig} instead.
* </ul>
* </p>
*
* @author groldan
* @see GeoServerInitializer
*/
public class GWCInitializer implements GeoServerInitializer {
private static final Logger LOGGER = Logging.getLogger(GWCInitializer.class);
/**
* {@link WMSInfo#getMetadata() WMSInfo metadata} key used in < 2.1.2 to hold the gwc direct wms
* integration enablement flag
*/
static String WMS_INTEGRATION_ENABLED_KEY = "GWC_WMS_Integration";
private final GWCConfigPersister configPersister;
private final Catalog rawCatalog;
private final TileLayerCatalog tileLayerCatalog;
private ConfigurableBlobStore blobStore;
public GWCInitializer(GWCConfigPersister configPersister, Catalog rawCatalog,
TileLayerCatalog tileLayerCatalog) {
this.configPersister = configPersister;
this.rawCatalog = rawCatalog;
this.tileLayerCatalog = tileLayerCatalog;
}
/**
* @see org.geoserver.config.GeoServerInitializer#initialize(org.geoserver.config.GeoServer)
*/
public void initialize(final GeoServer geoServer) throws Exception {
LOGGER.info("Initializing GeoServer specific GWC configuration from "
+ GWCConfigPersister.GWC_CONFIG_FILE);
final Version currentVersion = new Version("1.1.0");
final Resource configFile = configPersister.findConfigFile();
if (configFile == null || configFile.getType() != Type.RESOURCE) {
LOGGER.fine("GWC's GeoServer specific configuration not found, creating with old defaults");
GWCConfig oldDefaults = GWCConfig.getOldDefaults();
oldDefaults.setVersion(currentVersion.toString());
upgradeWMSIntegrationConfig(geoServer, oldDefaults);
createDefaultTileLayerInfos(oldDefaults);
configPersister.save(oldDefaults);
}
final GWCConfig config = configPersister.getConfig();
final Version version = new Version(config.getVersion());
if (version.compareTo(new Version("2.0.0")) < 0 && config.isWMTSEnabled() != null) {
// setting WMTS enabling information based on old GWC configuration setting
WMTSInfo globalServiceInfo = geoServer.getFacade().getService(WMTSInfo.class);
globalServiceInfo.setEnabled(config.isWMTSEnabled());
geoServer.save(globalServiceInfo);
// overriding configuration
config.setWMTSEnabled(null);
configPersister.save(config);
}
if (currentVersion.compareTo(version) > 0) {
// got the global config file, so old defaults are already in place if need be. Now
// check whether we need to migrate the configuration from the Layer/GroupInfo metadata
// maps to the tile layer catalog store
moveTileLayerInfosToTileLayerCatalog();
config.setVersion(currentVersion.toString());
configPersister.save(config);
}
final GWCConfig gwcConfig = configPersister.getConfig();
checkNotNull(gwcConfig);
// Setting default CacheProvider class if not present
if(gwcConfig.getCacheProviderClass() == null || gwcConfig.getCacheProviderClass().isEmpty()){
gwcConfig.setCacheProviderClass(GuavaCacheProvider.class.toString());
configPersister.save(gwcConfig);
}
// Setting default Cache Configuration
if (gwcConfig.getCacheConfigurations() == null) {
if(LOGGER.isLoggable(Level.FINEST)){
LOGGER.finest("Setting default CacheConfiguration");
}
Map<String, CacheConfiguration> map = new HashMap<String, CacheConfiguration>();
map.put(GuavaCacheProvider.class.toString(), new CacheConfiguration());
gwcConfig.setCacheConfigurations(map);
configPersister.save(gwcConfig);
} else {
if(LOGGER.isLoggable(Level.FINEST)){
LOGGER.finest("CacheConfiguration loaded");
}
}
// Change ConfigurableBlobStore behavior
if (blobStore != null) {
String cacheProviderClass = gwcConfig.getCacheProviderClass();
if(!blobStore.getCacheProviders().containsKey(cacheProviderClass)){
gwcConfig.setCacheProviderClass(GuavaCacheProvider.class.toString());
configPersister.save(gwcConfig);
if(LOGGER.isLoggable(Level.FINEST)){
LOGGER.finest("Unable to find: "+ cacheProviderClass +", used default configuration");
}
}
blobStore.setChanged(gwcConfig, true);
CacheProvider cache = blobStore.getCache();
// Add all the various Layers to avoid caching
addLayersToNotCache(cache, gwcConfig);
}
}
/**
* Migrates the configuration of geoserver tile layers from the Layer/GroupInfo metadata maps to
* the {@link #tileLayerCatalog}
*/
private void moveTileLayerInfosToTileLayerCatalog() {
for (LayerInfo layer : rawCatalog.getLayers()) {
if (!CatalogConfiguration.isLayerExposable(layer)) {
continue;
}
try {
GeoServerTileLayerInfo tileLayerInfo;
tileLayerInfo = LegacyTileLayerInfoLoader.load(layer);
if (tileLayerInfo != null) {
tileLayerCatalog.save(tileLayerInfo);
MetadataMap metadata = layer.getMetadata();
LegacyTileLayerInfoLoader.clear(metadata);
rawCatalog.save(layer);
}
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING, "Error migrating GWC Tile Layer settings for Layer '"
+ layer.getName() + "'", e);
}
}
for (LayerGroupInfo layer : rawCatalog.getLayerGroups()) {
try {
GeoServerTileLayerInfo tileLayerInfo;
tileLayerInfo = LegacyTileLayerInfoLoader.load(layer);
if (tileLayerInfo != null) {
tileLayerCatalog.save(tileLayerInfo);
MetadataMap metadata = layer.getMetadata();
LegacyTileLayerInfoLoader.clear(metadata);
rawCatalog.save(layer);
}
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING,
"Error occurred saving default GWC Tile Layer settings for LayerGroup '"
+ tileLayerName(layer) + "'", e);
}
}
}
/**
* This method is called only the first time the {@link GWCConfig} is initialized and is used to
* maintain backwards compatibility with the old GWC defaults.
*/
private void createDefaultTileLayerInfos(final GWCConfig defaultSettings) {
checkArgument(defaultSettings.isSane());
for (LayerInfo layer : rawCatalog.getLayers()) {
if (!CatalogConfiguration.isLayerExposable(layer)) {
continue;
}
try {
GeoServerTileLayerInfo tileLayerInfo;
tileLayerInfo = TileLayerInfoUtil.loadOrCreate(layer, defaultSettings);
tileLayerCatalog.save(tileLayerInfo);
MetadataMap metadata = layer.getMetadata();
if (metadata.containsKey(LegacyTileLayerInfoLoader.CONFIG_KEY_ENABLED)) {
LegacyTileLayerInfoLoader.clear(metadata);
rawCatalog.save(layer);
}
} catch (RuntimeException e) {
LOGGER.log(
Level.WARNING,
"Error occurred saving default GWC Tile Layer settings for Layer '"
+ layer.getName() + "'", e);
}
}
for (LayerGroupInfo layer : rawCatalog.getLayerGroups()) {
try {
GeoServerTileLayerInfo tileLayerInfo;
tileLayerInfo = TileLayerInfoUtil.loadOrCreate(layer, defaultSettings);
tileLayerCatalog.save(tileLayerInfo);
MetadataMap metadata = layer.getMetadata();
if (metadata.containsKey(LegacyTileLayerInfoLoader.CONFIG_KEY_ENABLED)) {
LegacyTileLayerInfoLoader.clear(metadata);
rawCatalog.save(layer);
}
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING,
"Error occurred saving default GWC Tile Layer settings for LayerGroup '"
+ tileLayerName(layer) + "'", e);
}
}
}
/**
* Before using {@code gwc-gs.xml} to hold the integrated GWC configuration, the only property
* configured was whether the direct WMS integration option was enabled, and it was saved as
* part of the {@link WMSInfo} metadata map under the {@code GWC_WMS_Integration} key. This
* method removes that key from WMSInfo if present and sets its value to the {@code gwcConfig}
* instead.
*/
private void upgradeWMSIntegrationConfig(final GeoServer geoServer, final GWCConfig gwcConfig)
throws IOException {
// Check whether we're using the old way of storing this information, and get rid of it
WMSInfo service = geoServer.getService(WMSInfo.class);
if (service != null) {
MetadataMap metadata = service.getMetadata();
if (service != null && metadata != null) {
Boolean storedValue = metadata.get(WMS_INTEGRATION_ENABLED_KEY, Boolean.class);
if (storedValue != null) {
boolean enabled = storedValue.booleanValue();
gwcConfig.setDirectWMSIntegrationEnabled(enabled);
metadata.remove(WMS_INTEGRATION_ENABLED_KEY);
geoServer.save(service);
}
}
}
}
/**
* Private method for adding all the Layer that must not be cached to the {@link CacheProvider} instance.
*
* @param cache
* @param defaultSettings
*/
private void addLayersToNotCache(CacheProvider cache, GWCConfig defaultSettings) {
if(LOGGER.isLoggable(Level.FINEST)){
LOGGER.finest("Adding Layers to avoid In Memory Caching");
}
List<PublishedInfo> publisheds = new ArrayList<>(rawCatalog.getLayers());
publisheds.addAll(rawCatalog.getLayerGroups());
publisheds.parallelStream().forEach(layer -> {
try {
// Check if the Layer must not be cached
GeoServerTileLayerInfo tileLayerInfo = tileLayerCatalog.getLayerById(layer.getId());
if (tileLayerInfo != null && tileLayerInfo.isEnabled()
&& !tileLayerInfo.isInMemoryCached()) {
// Add it to the cache
synchronized(cache) {
cache.addUncachedLayer(tileLayerInfo.getName());
}
}
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING, "Error occurred retrieving Layer '" + layer.getName()
+ "'", e);
}
});
}
/**
* Setter for the blobStore parameter
*
* @param blobStore
*/
public void setBlobStore(ConfigurableBlobStore blobStore) {
this.blobStore = blobStore;
}
}