From 745eba1ffad012dc0dce6598263d9c2164f55ba5 Mon Sep 17 00:00:00 2001 From: Gabriel Roldan Date: Sun, 13 Oct 2024 11:33:40 -0300 Subject: [PATCH] Configure GWC lock provider with the cluster-aware geoserver lock provider --- .../core/GwcCoreAutoConfigurationTest.java | 36 +++++++++++ .../core/GeoWebCacheCoreConfiguration.java | 23 ++++++- .../gwc/config/AbstractGwcInitializer.java | 64 ++++++++++++++----- 3 files changed, 104 insertions(+), 19 deletions(-) diff --git a/src/gwc/autoconfigure/src/test/java/org/geoserver/cloud/autoconfigure/gwc/core/GwcCoreAutoConfigurationTest.java b/src/gwc/autoconfigure/src/test/java/org/geoserver/cloud/autoconfigure/gwc/core/GwcCoreAutoConfigurationTest.java index dbc2d0bb5..d6cf35b56 100644 --- a/src/gwc/autoconfigure/src/test/java/org/geoserver/cloud/autoconfigure/gwc/core/GwcCoreAutoConfigurationTest.java +++ b/src/gwc/autoconfigure/src/test/java/org/geoserver/cloud/autoconfigure/gwc/core/GwcCoreAutoConfigurationTest.java @@ -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; @@ -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( diff --git a/src/gwc/core/src/main/java/org/geoserver/cloud/gwc/config/core/GeoWebCacheCoreConfiguration.java b/src/gwc/core/src/main/java/org/geoserver/cloud/gwc/config/core/GeoWebCacheCoreConfiguration.java index 0ed23a98a..fd65e5e1f 100644 --- a/src/gwc/core/src/main/java/org/geoserver/cloud/gwc/config/core/GeoWebCacheCoreConfiguration.java +++ b/src/gwc/core/src/main/java/org/geoserver/cloud/gwc/config/core/GeoWebCacheCoreConfiguration.java @@ -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; @@ -61,6 +64,17 @@ @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(); @@ -283,9 +297,12 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha * */ 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"; diff --git a/src/gwc/core/src/main/java/org/geoserver/gwc/config/AbstractGwcInitializer.java b/src/gwc/core/src/main/java/org/geoserver/gwc/config/AbstractGwcInitializer.java index ac3e8600b..3bdd25f36 100644 --- a/src/gwc/core/src/main/java/org/geoserver/gwc/config/AbstractGwcInitializer.java +++ b/src/gwc/core/src/main/java/org/geoserver/gwc/config/AbstractGwcInitializer.java @@ -18,11 +18,13 @@ import org.geoserver.config.GeoServer; import org.geoserver.config.GeoServerReinitializer; import org.geoserver.gwc.ConfigurableBlobStore; +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; @@ -58,10 +60,18 @@ @RequiredArgsConstructor 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 configLock; + protected final @NonNull GeoServerConfigurationLock globalConfigLock; protected abstract Logger logger(); @@ -93,23 +103,45 @@ public void initialize(final GeoServer geoServer) throws Exception { * #initialize(org.geoserver.config.GeoServer) super.initialize(GeoServer)} */ private void initializeGeoServerIntegrationConfigFile() throws IOException { - if (configFileExists()) { - return; + 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(); } - final boolean lockAcquired = configLock.tryLock(LockType.WRITE); - if (lockAcquired) { - try { - if (!configFileExists()) { - logger().info( - "Initializing GeoServer specific GWC configuration {}", - configPersister.findConfigFile()); - GWCConfig defaults = new GWCConfig(); - defaults.setVersion("1.1.0"); - configPersister.save(defaults); - } - } finally { - configLock.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. + * + *

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); } }