Skip to content

Commit

Permalink
Merge pull request #549 from groldan/gwc/config_events
Browse files Browse the repository at this point in the history
Distributed event notification of GWC config changes
  • Loading branch information
groldan authored Oct 14, 2024
2 parents af47cd2 + 6956009 commit 41650d9
Show file tree
Hide file tree
Showing 13 changed files with 366 additions and 73 deletions.
2 changes: 1 addition & 1 deletion config
Submodule config updated 1 files
+3 −0 geoserver_logging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import org.geoserver.GeoServerConfigurationLock;
import org.geoserver.cloud.gwc.backend.pgconfig.PgconfigTileLayerCatalog;
import org.geoserver.cloud.gwc.config.core.AbstractGwcInitializer;
import org.geoserver.cloud.gwc.event.TileLayerEvent;
import org.geoserver.cloud.gwc.repository.GeoServerTileLayerConfiguration;
import org.geoserver.config.GeoServerReinitializer;
import org.geoserver.gwc.ConfigurableBlobStore;
import org.geoserver.gwc.config.AbstractGwcInitializer;
import org.geoserver.gwc.config.GWCConfigPersister;
import org.geoserver.gwc.config.GWCInitializer;
import org.geoserver.gwc.layer.TileLayerCatalog;
Expand Down Expand Up @@ -46,8 +47,9 @@ class PgconfigGwcInitializer extends AbstractGwcInitializer {
public PgconfigGwcInitializer(
@NonNull GWCConfigPersister configPersister,
@NonNull ConfigurableBlobStore blobStore,
@NonNull GeoServerTileLayerConfiguration geoseverTileLayers) {
super(configPersister, blobStore, geoseverTileLayers);
@NonNull GeoServerTileLayerConfiguration geoseverTileLayers,
@NonNull GeoServerConfigurationLock configLock) {
super(configPersister, blobStore, geoseverTileLayers, configLock);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import org.geoserver.GeoServerConfigurationLock;
import org.geoserver.catalog.Catalog;
import org.geoserver.cloud.autoconfigure.catalog.backend.pgconfig.ConditionalOnPgconfigBackendEnabled;
import org.geoserver.cloud.autoconfigure.catalog.backend.pgconfig.PgconfigDataSourceAutoConfiguration;
Expand Down Expand Up @@ -58,13 +59,18 @@ void log() {
log.info("GeoWebCache TileLayerCatalog using PostgreSQL config backend");
}

/** Replacement for {@link GWCInitializer} when using {@link GeoServerTileLayerConfiguration} */
/**
* Replacement for {@link GWCInitializer} when using {@link GeoServerTileLayerConfiguration}
*
* @param configLock
*/
@Bean
PgconfigGwcInitializer gwcInitializer(
GWCConfigPersister configPersister,
ConfigurableBlobStore blobStore,
GeoServerTileLayerConfiguration tileLayerCatalog) {
return new PgconfigGwcInitializer(configPersister, blobStore, tileLayerCatalog);
GeoServerTileLayerConfiguration tileLayerCatalog,
GeoServerConfigurationLock configLock) {
return new PgconfigGwcInitializer(configPersister, blobStore, tileLayerCatalog, configLock);
}

@Bean(name = "gwcCatalogConfiguration")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@
import org.geoserver.cloud.gwc.repository.CloudDefaultStorageFinder;
import org.geoserver.cloud.gwc.repository.CloudGwcXmlConfiguration;
import org.geoserver.cloud.gwc.repository.CloudXMLResourceProvider;
import org.geoserver.gwc.ConfigurableLockProvider;
import org.geoserver.gwc.GWC;
import org.geoserver.gwc.GeoServerLockProvider;
import org.geoserver.gwc.config.AbstractGwcInitializer;
import org.geoserver.gwc.config.DefaultGwcInitializer;
import org.geoserver.gwc.config.GWCConfig;
import org.geoserver.gwc.config.GWCConfigPersister;
import org.geoserver.platform.GeoServerExtensionsHelper;
import org.geowebcache.locks.LockProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
Expand Down Expand Up @@ -65,6 +72,35 @@ void defaultCacheDirectoryIsAFile(@TempDir File tmpDir) throws IOException {
assertContextLoadFails(BeanInitializationException.class, "is not a directory");
}

@Test
void lockProviderDelegatesStoGeoSeverLockProvider() {
runner.run(
context -> {
GeoServerExtensionsHelper.init(context);
assertThat(context)
.hasNotFailed()
.hasBean(AbstractGwcInitializer.GWC_LOCK_PROVIDER_BEAN_NAME)
.getBean(AbstractGwcInitializer.GWC_LOCK_PROVIDER_BEAN_NAME)
.isInstanceOf(GeoServerLockProvider.class);

GWCConfigPersister persister = context.getBean(GWCConfigPersister.class);
GWCConfig config = persister.getConfig();
assertThat(config.getLockProviderName())
.isEqualTo(AbstractGwcInitializer.GWC_LOCK_PROVIDER_BEAN_NAME);

GWC gwc = GWC.get();

LockProvider lockProvider = gwc.getLockProvider();
assertThat(lockProvider).isInstanceOf(ConfigurableLockProvider.class);
GeoServerLockProvider expected =
context.getBean(
AbstractGwcInitializer.GWC_LOCK_PROVIDER_BEAN_NAME,
GeoServerLockProvider.class);
assertThat(((ConfigurableLockProvider) lockProvider).getDelegate())
.isSameAs(expected);
});
}

@Test
void contextLoads() {
runner.run(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
import lombok.extern.slf4j.Slf4j;

import org.geoserver.cloud.config.factory.FilteringXmlBeanDefinitionReader;
import org.geoserver.cloud.gwc.event.ConfigChangeEvent;
import org.geoserver.config.util.XStreamPersisterFactory;
import org.geoserver.gwc.config.CloudGwcConfigPersister;
import org.geoserver.gwc.config.GWCConfigPersister;
import org.geoserver.platform.GeoServerResourceLoader;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

Expand All @@ -32,6 +39,7 @@ public class GeoServerIntegrationConfiguration {
|gwcTransactionListener\
|gwcWMSExtendedCapabilitiesProvider\
|gwcInitializer\
|gwcGeoServervConfigPersister\
).*$\
""";

Expand All @@ -42,4 +50,20 @@ public class GeoServerIntegrationConfiguration {
public void log() {
log.info("GeoWebCache core GeoServer integration enabled");
}

/**
* Overrides {@code gwcGeoServervConfigPersister} with a cluster-aware {@link
* GWCConfigPersister} that sends {@link ConfigChangeEvent}s upon {@link
* GWCConfigPersister#save(org.geoserver.gwc.config.GWCConfig)}
*
* @param xsfp
* @param resourceLoader
*/
@Bean
GWCConfigPersister gwcGeoServervConfigPersister(
XStreamPersisterFactory xsfp,
GeoServerResourceLoader resourceLoader,
ApplicationEventPublisher publisher) {
return new CloudGwcConfigPersister(xsfp, resourceLoader, publisher::publishEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
import org.geoserver.cloud.gwc.repository.CloudDefaultStorageFinder;
import org.geoserver.cloud.gwc.repository.CloudGwcXmlConfiguration;
import org.geoserver.cloud.gwc.repository.CloudXMLResourceProvider;
import org.geoserver.gwc.GeoServerLockProvider;
import org.geoserver.gwc.config.AbstractGwcInitializer;
import org.geoserver.platform.resource.Resource;
import org.geoserver.platform.resource.ResourceStore;
import org.geoserver.platform.resource.Resources;
import org.geowebcache.config.ConfigurationResourceProvider;
import org.geowebcache.config.XMLConfiguration;
import org.geowebcache.config.XMLFileResourceProvider;
import org.geowebcache.locks.LockProvider;
import org.geowebcache.storage.DefaultStorageFinder;
import org.geowebcache.util.ApplicationContextProvider;
import org.springframework.beans.FatalBeanException;
Expand Down Expand Up @@ -56,11 +59,22 @@
@ImportResource(
reader = FilteringXmlBeanDefinitionReader.class, //
locations = {
"jar:gs-gwc-[0-9]+.*!/geowebcache-core-context.xml#name=^(?!gwcXmlConfig|gwcDefaultStorageFinder|gwcGeoServervConfigPersister|metastoreRemover).*$"
"jar:gs-gwc-[0-9]+.*!/geowebcache-core-context.xml#name=^(?!gwcXmlConfig|gwcDefaultStorageFinder|metastoreRemover).*$"
})
@Slf4j(topic = "org.geoserver.cloud.gwc.config.core")
public class GeoWebCacheCoreConfiguration {

/**
* @return a {@link GeoServerLockProvider} delegating the the {@link ResourceStore}, whose
* {@link ResourceStore#getLockProvider()} is known to be cluster-capable
*/
@Bean(name = AbstractGwcInitializer.GWC_LOCK_PROVIDER_BEAN_NAME)
LockProvider gwcLockProvider(@Qualifier("resourceStoreImpl") ResourceStore resourceStore) {
var provider = new GeoServerLockProvider();
provider.setDelegate(resourceStore);
return provider;
}

@Bean
SetRequestPathInfoFilter setRequestPathInfoFilter() {
return new SetRequestPathInfoFilter();
Expand Down Expand Up @@ -283,9 +297,12 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
* </ul>
*/
protected ServletRequest adaptRequest(HttpServletRequest request) {
// full request URI (e.g. '/geoserver/cloud/{workspace}/gwc/service/tms/1.0.0', where
// '/geoserver/cloud' is the context path as given by the gateway's base uri, and
// '/{workspace}/gwc' the suffix after which comes the pathInfo '/service/tms/1.0.0')
// full request URI (e.g. '/geoserver/cloud/{workspace}/gwc/service/tms/1.0.0',
// where
// '/geoserver/cloud' is the context path as given by the gateway's base uri,
// and
// '/{workspace}/gwc' the suffix after which comes the pathInfo
// '/service/tms/1.0.0')
final String requestURI = request.getRequestURI();

final String gwc = "/gwc";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.geoserver.cloud.gwc.event;

@SuppressWarnings("serial")
public class ConfigChangeEvent extends GeoWebCacheEvent {

public static final String OBJECT_ID = "gwcConfig";

public ConfigChangeEvent(Object source) {
super(source, Type.MODIFIED);
}

@Override
protected String getObjectId() {
return OBJECT_ID;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.gwc.config.core;
package org.geoserver.gwc.config;

import static com.google.common.base.Preconditions.checkNotNull;

Expand All @@ -11,21 +11,25 @@
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import org.geoserver.GeoServerConfigurationLock;
import org.geoserver.GeoServerConfigurationLock.LockType;
import org.geoserver.cloud.gwc.event.TileLayerEvent;
import org.geoserver.cloud.gwc.repository.GeoServerTileLayerConfiguration;
import org.geoserver.config.GeoServer;
import org.geoserver.config.GeoServerReinitializer;
import org.geoserver.gwc.ConfigurableBlobStore;
import org.geoserver.gwc.config.GWCConfig;
import org.geoserver.gwc.config.GWCConfigPersister;
import org.geoserver.gwc.config.GWCInitializer;
import org.geoserver.gwc.GWC;
import org.geoserver.gwc.layer.GeoServerTileLayer;
import org.geoserver.gwc.layer.TileLayerCatalog;
import org.geoserver.platform.resource.Resource;
import org.geoserver.platform.resource.Resources;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.locks.LockProvider;
import org.geowebcache.storage.blobstore.memory.CacheConfiguration;
import org.geowebcache.storage.blobstore.memory.CacheProvider;
import org.geowebcache.storage.blobstore.memory.guava.GuavaCacheProvider;
import org.slf4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.event.EventListener;
import org.springframework.util.StringUtils;

Expand Down Expand Up @@ -54,14 +58,28 @@
* @since 1.8
*/
@RequiredArgsConstructor
public abstract class AbstractGwcInitializer implements GeoServerReinitializer {
public abstract class AbstractGwcInitializer implements GeoServerReinitializer, InitializingBean {

/**
* {@link GWC#saveConfig(GWCConfig)} will lookup for the {@link LockProvider} named after {@link
* GWCConfig#getLockProviderName()}. We need it to be a cluster-aware lock provider. This is the
* bean name to be registered by the configuration, and we'll set it to {@link
* GWCConfig#setLockProviderName(String)} during initialization.
*/
public static final String GWC_LOCK_PROVIDER_BEAN_NAME = "gwcClusteringLockProvider";

protected final @NonNull GWCConfigPersister configPersister;
protected final @NonNull ConfigurableBlobStore blobStore;
protected final @NonNull GeoServerTileLayerConfiguration geoseverTileLayers;
protected final @NonNull GeoServerConfigurationLock globalConfigLock;

protected abstract Logger logger();

@Override
public void afterPropertiesSet() throws IOException {
initializeGeoServerIntegrationConfigFile();
}

/**
* @see org.geoserver.config.GeoServerInitializer#initialize(org.geoserver.config.GeoServer)
*/
Expand All @@ -80,6 +98,58 @@ public void initialize(final GeoServer geoServer) throws Exception {
setUpNonMemoryCacheableLayers();
}

/**
* Initialize the datadir/gs-gwc.xml file before {@link
* #initialize(org.geoserver.config.GeoServer) super.initialize(GeoServer)}
*/
private void initializeGeoServerIntegrationConfigFile() throws IOException {
globalConfigLock.lock(LockType.WRITE);
try {
if (configFileExists()) {
updateLockProviderName();
} else {
logger().info(
"Initializing GeoServer specific GWC configuration {}",
configPersister.findConfigFile());
GWCConfig defaults = new GWCConfig();
defaults.setVersion("1.1.0");
defaults.setLockProviderName(GWC_LOCK_PROVIDER_BEAN_NAME);
configPersister.save(defaults);
}
} finally {
globalConfigLock.unlock();
}
}

/**
* In case the {@link GWCConfig} exists and its lock provider name is not {@link
* #GWC_LOCK_PROVIDER_BEAN_NAME}, updates and saves the configuration.
*
* <p>At this point, {@link #configFileExists()} is known to be true.
*/
private void updateLockProviderName() throws IOException {
final GWCConfig gwcConfig = configPersister.getConfig();
if (!GWC_LOCK_PROVIDER_BEAN_NAME.equals(gwcConfig.getLockProviderName())) {
if (null == gwcConfig.getLockProviderName()) {
logger().info(
"Setting GeoWebCache lock provider to {}",
GWC_LOCK_PROVIDER_BEAN_NAME);
} else {
logger().warn(
"Updating GeoWebCache lock provider from {} to {}",
gwcConfig.getLockProviderName(),
GWC_LOCK_PROVIDER_BEAN_NAME);
}
gwcConfig.setLockProviderName(GWC_LOCK_PROVIDER_BEAN_NAME);
configPersister.save(gwcConfig);
}
}

private boolean configFileExists() throws IOException {
Resource configFile = configPersister.findConfigFile();
return Resources.exists(configFile);
}

@EventListener(TileLayerEvent.class)
void onTileLayerEvent(TileLayerEvent event) {
cacheProvider().ifPresent(cache -> onTileLayerEvent(event, cache));
Expand Down
Loading

0 comments on commit 41650d9

Please sign in to comment.