Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for GWC not storing tiles when using datadir or jdbcconfig catalog back-ends #511

Merged
merged 1 commit into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/gwc/autoconfigure/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,10 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@

/**
* {@link AutoConfiguration @AutoConfiguration} to set up the GeoServer {@link TileLayerCatalog}
* using the default implementation based on the {@link ResourceStore}
* using the default implementation based on the {@link ResourceStore}.
*
* <p>This default configuration applies if there's no other {@link GeoServerTileLayerConfiguration}
* provided.
*
* @see DefaultTileLayerCatalogConfiguration
* @see ConditionalOnGeoWebCacheEnabled
* @see PgconfigTileLayerCatalogAutoConfiguration
* @since 1.0
*/
@AutoConfiguration(after = PgconfigTileLayerCatalogAutoConfiguration.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,22 @@
*/
package org.geoserver.cloud.autoconfigure.gwc.backend;

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

import com.google.common.base.Stopwatch;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

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.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.layer.GeoServerTileLayer;
import org.geoserver.gwc.layer.TileLayerCatalog;
import org.geowebcache.config.TileLayerConfiguration;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.layer.TileLayerDispatcher;
import org.geowebcache.storage.blobstore.memory.CacheConfiguration;
import org.geowebcache.storage.blobstore.memory.CacheProvider;
import org.geowebcache.storage.blobstore.memory.guava.GuavaCacheProvider;
import org.springframework.context.event.EventListener;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.slf4j.Logger;

/**
* Replacement for {@link GWCInitializer} when using the "pgconfig" storage.
Expand All @@ -57,151 +40,18 @@
*
* @since 1.8
*/
@RequiredArgsConstructor
@Slf4j(topic = "org.geoserver.cloud.autoconfigure.gwc.backend")
class PgconfigGwcInitializer implements GeoServerReinitializer {

private final @NonNull GWCConfigPersister configPersister;
private final @NonNull ConfigurableBlobStore blobStore;
private final @NonNull GeoServerTileLayerConfiguration geoseverTileLayers;

/**
* @see org.geoserver.config.GeoServerInitializer#initialize(org.geoserver.config.GeoServer)
*/
@Override
public void initialize(final GeoServer geoServer) throws Exception {
log.info("Initializing GeoServer specific GWC configuration from gwc-gs.xml");

final GWCConfig gwcConfig = configPersister.getConfig();
checkNotNull(gwcConfig);
class PgconfigGwcInitializer extends AbstractGwcInitializer {

configureInMemoryCacheProvider(gwcConfig);

final boolean initialization = true;
blobStore.setChanged(gwcConfig, initialization);

setUpNonMemoryCacheableLayers();
}

@EventListener(TileLayerEvent.class)
void onTileLayerEvent(TileLayerEvent event) {
cacheProvider()
.ifPresent(
cache -> {
switch (event.getEventType()) {
case DELETED:
log.debug(
"TileLayer {} deleted, notifying in-memory CacheProvider",
event.getName());
cache.removeUncachedLayer(event.getName());
break;
case MODIFIED:
if (event.getOldName() != null
&& !Objects.equals(
event.getOldName(), event.getName())) {
log.info(
"TileLayer {} renamed to {}, notifying in-memory CacheProvider",
event.getOldName(),
event.getName());
cache.removeUncachedLayer(event.getOldName());
}
setInMemoryLayerCaching(event.getName());
break;
default:
setInMemoryLayerCaching(event.getName());
break;
}
});
public PgconfigGwcInitializer(
@NonNull GWCConfigPersister configPersister,
@NonNull ConfigurableBlobStore blobStore,
@NonNull GeoServerTileLayerConfiguration geoseverTileLayers) {
super(configPersister, blobStore, geoseverTileLayers);
}

private void configureInMemoryCacheProvider(final GWCConfig gwcConfig) throws IOException {
// 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) {
log.debug("Setting default CacheConfiguration");
Map<String, CacheConfiguration> map = new HashMap<>();
map.put(GuavaCacheProvider.class.toString(), new CacheConfiguration());
gwcConfig.setCacheConfigurations(map);
configPersister.save(gwcConfig);
} else {
log.debug("CacheConfiguration loaded");
}

// Change ConfigurableBlobStore behavior
String cacheProviderClass = gwcConfig.getCacheProviderClass();
Map<String, CacheProvider> cacheProviders = blobStore.getCacheProviders();
if (!cacheProviders.containsKey(cacheProviderClass)) {
gwcConfig.setCacheProviderClass(GuavaCacheProvider.class.toString());
configPersister.save(gwcConfig);
log.debug("Unable to find: {}, used default configuration", cacheProviderClass);
}
}

private void setInMemoryLayerCaching(@NonNull String layerName) {

layer(layerName)
.ifPresentOrElse(this::addUncachedLayer, () -> removeCachedLayer(layerName));
}

private void removeCachedLayer(String layerName) {
cacheProvider()
.ifPresent(
cache -> {
log.debug(
"TileLayer {} does not exist, notifying CacheProvider",
layerName);
cache.removeLayer(layerName);
cache.removeUncachedLayer(layerName);
});
}

private void addUncachedLayer(GeoServerTileLayer tl) {
if (!tl.getInfo().isInMemoryCached()) {
log.debug(
"TileLayer {} is not to be memory cached, notifying CacheProvider",
tl.getName());
cacheProvider().ifPresent(cache -> cache.addUncachedLayer(tl.getName()));
}
}

private Optional<GeoServerTileLayer> layer(String layerName) {
return geoseverTileLayers
.getLayer(layerName)
.filter(GeoServerTileLayer.class::isInstance)
.map(GeoServerTileLayer.class::cast);
}

/**
* Private method for adding all the Layer that must not be cached to the {@link CacheProvider}
* instance.
*/
private void setUpNonMemoryCacheableLayers() {
cacheProvider()
.ifPresent(
cache -> {
// Add all the various Layers to avoid caching
log.info("Adding Layers to avoid In Memory Caching");
// it is ok to use the ForkJoinPool.commonPool() here, there's no I/O
// involved
Stopwatch sw = Stopwatch.createStarted();
Collection<? extends TileLayer> layers = geoseverTileLayers.getLayers();
log.info("Queried {} tile layers in {}", layers.size(), sw.stop());
layers.stream()
.filter(GeoServerTileLayer.class::isInstance)
.map(GeoServerTileLayer.class::cast)
.filter(l -> l.isEnabled() && !l.getInfo().isInMemoryCached())
.map(GeoServerTileLayer::getName)
.forEach(cache::addUncachedLayer);
});
}

private Optional<CacheProvider> cacheProvider() {
return Optional.ofNullable(blobStore.getCache());
@Override
protected Logger logger() {
return log;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* GeoServer Catalog in the Postgres database;
*
* @since 1.7
* @see ConditionalOnPgconfigBackendEnabled
*/
@AutoConfiguration(after = PgconfigDataSourceAutoConfiguration.class)
@ConditionalOnClass(PgconfigTileLayerCatalog.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/
package org.geoserver.cloud.autoconfigure.gwc.backend;

import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
Expand All @@ -14,6 +16,7 @@
import com.google.common.base.Throwables;

import org.geoserver.cloud.autoconfigure.gwc.GeoWebCacheContextRunner;
import org.geoserver.cloud.gwc.config.core.GeoWebCacheConfigurationProperties;
import org.geoserver.cloud.gwc.repository.CloudDefaultStorageFinder;
import org.geoserver.cloud.gwc.repository.CloudGwcXmlConfiguration;
import org.geoserver.cloud.gwc.repository.CloudXMLResourceProvider;
Expand All @@ -37,6 +40,7 @@ class GwcCoreAutoConfigurationTest {

@BeforeEach
void setUp() {
System.clearProperty("GEOWEBCACHE_CACHE_DIR");
runner = GeoWebCacheContextRunner.newMinimalGeoWebCacheContextRunner(tmpDir);
}

Expand Down Expand Up @@ -92,4 +96,54 @@ protected void assertContextLoadFails(
assertThat(root.getMessage(), containsString(expectedMessage));
});
}

/**
* Note this test will fail without {@code --add-opens=java.base/java.util=ALL-UNNAMED} JVM
* parameter (watch out if running it from the IDE, maven is configured to set it)
*/
@Test
void defaultCacheDirectoryFromEnvVariable() throws Exception {
File dir = new File(tmpDir, "env_cachedir");
assertThat(dir).doesNotExist();
String dirpath = dir.getAbsolutePath();
withEnvironmentVariable("GEOWEBCACHE_CACHE_DIR", dirpath)
.execute(
() -> {
assertThat(System.getenv("GEOWEBCACHE_CACHE_DIR")).isEqualTo(dirpath);
runner.withPropertyValues(
"gwc.cache-directory: ${GEOWEBCACHE_CACHE_DIR}")
.run(
context -> {
assertThat(context).hasNotFailed();
assertThat(dir).isDirectory();

var gwcConfigProps =
context.getBean(
GeoWebCacheConfigurationProperties
.class);
assertThat(gwcConfigProps.getCacheDirectory())
.isEqualTo(dir.toPath());
});
});
assertThat(System.getenv("GEOWEBCACHE_CACHE_DIR")).isNull();
}

@Test
void defaultCacheDirectoryFromSystemProperty() throws Exception {
File dir = new File(tmpDir, "sysprop_cachedir");
assertThat(dir).doesNotExist();
String dirpath = dir.getAbsolutePath();

System.setProperty("GEOWEBCACHE_CACHE_DIR", dirpath);
try {
runner.withPropertyValues("gwc.cache-directory: ${GEOWEBCACHE_CACHE_DIR}")
.run(
context -> {
assertThat(context).hasNotFailed();
assertThat(dir).isDirectory();
});
} finally {
System.clearProperty("GEOWEBCACHE_CACHE_DIR");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
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.config.GwcGeoserverConfigurationInitializer;
import org.geoserver.gwc.config.DefaultGwcInitializer;
import org.geoserver.platform.GeoServerExtensionsHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -72,11 +72,9 @@ void contextLoads() {
GeoServerExtensionsHelper.init(context);
assertThat(context)
.hasNotFailed()
.hasBean("gwcGeoserverConfigurationInitializer")
.getBean(
"gwcGeoserverConfigurationInitializer",
GwcGeoserverConfigurationInitializer.class)
.isInstanceOf(GwcGeoserverConfigurationInitializer.class);
.hasBean("gwcInitializer")
.getBean("gwcInitializer")
.isInstanceOf(DefaultGwcInitializer.class);

assertThat(context.isTypeMatch("gwcXmlConfig", CloudGwcXmlConfiguration.class))
.isTrue();
Expand Down
Loading
Loading