Skip to content

Commit

Permalink
[jmaps-core] dir cache refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
mikey75 committed Oct 1, 2024
1 parent 6564a3b commit 3a63b71
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 48 deletions.
7 changes: 7 additions & 0 deletions jmaps-viewer/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.5.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ protected BaseCache() {
protected BaseCache(Path baseDir, Duration cacheTimeout) {
this.baseDir = baseDir;
this.cacheTimeout = cacheTimeout;
log.info("Secondary Tile Cache: {}", getClass().getSimpleName());
log.info("Secondary Tile Cache: {}, location: {}", getClass().getSimpleName(), getBaseDir());

if (!cacheTimeout.isZero()) {
log.info("Cache expiration checking enabled! Tiles will be re-downloaded every: {}", cacheTimeout);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import java.awt.image.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URI;
Expand All @@ -29,33 +29,47 @@ public DirectoryBasedCache(Path cacheDir, Duration cacheTimeout) {

@Override
public BufferedImage get(String key) {
return getImage(key);
}

@Override
public void put(String key, BufferedImage b) {
putImage(key, b);
}

private BufferedImage getImage(String key) {
try {
File f = getLocalFile(key);
return ImageIO.read(Files.newInputStream(f.toPath()));
Path filePath = getLocalFile(key).toPath();
// if file does not exists - return immediately
if (!filePath.toFile().exists()) {
return null;
} else {
return ImageIO.read(Files.newInputStream(filePath));
}
} catch (IOException e) {
// exception on file read - this might be emergency - file exists but cannot be read, warn
log.warn("File cache get failed for {}", key);
return null;
}
}


@Override
public void put(String key, BufferedImage b) {
private void putImage(String key, BufferedImage b) {
try {
File file = getLocalFile(key);
if (!file.exists()) {
Files.createDirectories(file.toPath());
Path filePath = getLocalFile(key).toPath();
// create file only if it does not exist so that if the entry expires,
// file is not recreated (saves time) and only image is written to this existing file
if (!filePath.toFile().exists()) {
Files.createDirectories(filePath);
}
writeImageToFile(b, file);
ImageIO.write(b, "png", filePath.toFile());

} catch (IOException ex) {
log.error("File cache put failed for {}", key, ex);
// exception on file put might be a filesystem issue emergency - warn
log.warn("File cache put failed for {}", key);
}
}

void writeImageToFile(BufferedImage image, File file) throws IOException {
ImageIO.write(image, "png", file);
}

public boolean keyExpired(String key) {

long now = System.currentTimeMillis();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.awaitility.Awaitility;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import javax.imageio.ImageIO;
import java.awt.image.*;
Expand All @@ -25,7 +27,7 @@
*/
class DirectoryBasedCacheTest {

private static final Path CACHE_DIR = Paths.get("target/testcache/tile-cache");
private static final Path TEST_CACHE_DIR = Paths.get("target/testcache/tile-cache");
private static final Duration SHORT_TIMEOUT_FOR_VALIDITY_TESTS = Duration.ofSeconds(2);

private static final File TEST_IMAGE_FILE = new File("src/test/resources/tiles/tile.png");
Expand All @@ -39,31 +41,38 @@ class DirectoryBasedCacheTest {
private static final String WMTS_URL = "http://localhost/wmts?Service=WMTS&Request=GetTile&Layer=X&TileMatrixSet=Z&TileMatrix=Z:1&TileRow=1&TileCol=1";
private static final String LONG_URL = "https://dupa/z?=" + TestUtils.getRandomString(300);
private static final String GENERIC_URL = "https://paka.pl/10/10/10.png";
private static DirectoryBasedCache cache;

@BeforeEach
void init() throws IOException {

FileUtils.deleteDirectory(CACHE_DIR.toFile());
// delete test cache dir
FileUtils.deleteDirectory(TEST_CACHE_DIR.toFile());
TEST_IMAGE = ImageIO.read(TEST_IMAGE_FILE);
TEST_IMAGE_OTHER = ImageIO.read(TEST_IMAGE_OTHER_FILE);
}

@Test
void testDefaultCacheSettings() {
cache = new DirectoryBasedCache();
DirectoryBasedCache cache = new DirectoryBasedCache();
assertThat(cache.getCacheTimeout()).isEqualTo(Defaults.DEFAULT_CACHE_TIMEOUT);
assertThat(cache.getBaseDir()).isEqualTo(Defaults.DEFAULT_TILECACHE_DIR);
}

@Test
void shouldNotCacheFileIfIOErrorOnWrite() throws IOException {
cache = spy(new DirectoryBasedCache());
doThrow(IOException.class).when(cache).writeImageToFile(any(), any());
void testCustomCacheSettings() {
DirectoryBasedCache cache = new DirectoryBasedCache(TEST_CACHE_DIR, Duration.ofSeconds(2));
assertThat(cache.getCacheTimeout()).isEqualTo(Duration.ofSeconds(2));
assertThat(cache.getBaseDir()).isEqualTo(TEST_CACHE_DIR);
}

// put in cache, this should fail now
cache.put(XYZ_URL_WITH_QUERY, TEST_IMAGE);
@Test
void shouldNotCacheFileIfIOErrorOnWrite() {
DirectoryBasedCache cache = new DirectoryBasedCache(TEST_CACHE_DIR, Duration.ofSeconds(2));

try (MockedStatic<ImageIO> imageio = Mockito.mockStatic(ImageIO.class)) {
imageio.when(() -> ImageIO.write(any(BufferedImage.class), any(), any(File.class))).thenThrow(IOException.class);
// put in cache, this should fail now
cache.put(XYZ_URL_WITH_QUERY, TEST_IMAGE);
}
// assert file not in cache
assertThat(cache.get(XYZ_URL_WITH_QUERY)).isNull();

Expand All @@ -72,32 +81,32 @@ void shouldNotCacheFileIfIOErrorOnWrite() throws IOException {
@Test
void testCachePutWithDifferentUrlSchemas() {

cache = new DirectoryBasedCache(CACHE_DIR, Defaults.DEFAULT_CACHE_TIMEOUT);
DirectoryBasedCache cache = new DirectoryBasedCache(TEST_CACHE_DIR, Defaults.DEFAULT_CACHE_TIMEOUT);
cache.put(XYZ_URL_WITHOUT_QUERY, TEST_IMAGE);
cache.put(XYZ_URL_WITH_QUERY, TEST_IMAGE);
cache.put(WMTS_URL, TEST_IMAGE);
cache.put(LONG_URL, TEST_IMAGE);

retrieveFromCacheAndCheckContent(XYZ_URL_WITHOUT_QUERY, TEST_IMAGE);
retrieveFromCacheAndCheckContent(XYZ_URL_WITH_QUERY, TEST_IMAGE);
retrieveFromCacheAndCheckContent(WMTS_URL, TEST_IMAGE);
retrieveFromCacheAndCheckContent(LONG_URL, TEST_IMAGE);
retrieveFromCacheAndCheckContent(cache, XYZ_URL_WITHOUT_QUERY, TEST_IMAGE);
retrieveFromCacheAndCheckContent(cache, XYZ_URL_WITH_QUERY, TEST_IMAGE);
retrieveFromCacheAndCheckContent(cache, WMTS_URL, TEST_IMAGE);
retrieveFromCacheAndCheckContent(cache, LONG_URL, TEST_IMAGE);

}


@Test
void testGetNonExisting() {
// if getting file that does not exist in cache, get should return null
cache = new DirectoryBasedCache(CACHE_DIR, Defaults.DEFAULT_CACHE_TIMEOUT);
DirectoryBasedCache cache = new DirectoryBasedCache(TEST_CACHE_DIR, Defaults.DEFAULT_CACHE_TIMEOUT);
String NON_EXISTING_URL = "nonexisting";
assertThat(cache.get(NON_EXISTING_URL)).isNull();
}

@Test
void testTimeoutZero() {
// if cache timeout is zero, expiration check should never be called
cache = spy(new DirectoryBasedCache(CACHE_DIR, Duration.ZERO));
DirectoryBasedCache cache = spy(new DirectoryBasedCache(TEST_CACHE_DIR, Duration.ZERO));

cache.put(GENERIC_URL, TEST_IMAGE);
cache.get(GENERIC_URL);
Expand All @@ -107,7 +116,7 @@ void testTimeoutZero() {

@Test
void shouldCheckCacheEntryValidity() {
cache = new DirectoryBasedCache(CACHE_DIR, SHORT_TIMEOUT_FOR_VALIDITY_TESTS);
DirectoryBasedCache cache = new DirectoryBasedCache(TEST_CACHE_DIR, SHORT_TIMEOUT_FOR_VALIDITY_TESTS);
// should be valid right after putting in
cache.put(GENERIC_URL, TEST_IMAGE);
assertThat(cache.keyExpired(GENERIC_URL)).isFalse();
Expand All @@ -118,7 +127,7 @@ void shouldCheckCacheEntryValidity() {

@Test
void shouldNeverInvalidateCacheEntryWhenCacheTimeoutZero() throws InterruptedException {
cache = new DirectoryBasedCache(CACHE_DIR,Duration.ZERO);
DirectoryBasedCache cache = new DirectoryBasedCache(TEST_CACHE_DIR,Duration.ZERO);
// check for 2 seconds every 200ms
// to check if it is always not expired when time passes
long end = System.currentTimeMillis() + Duration.ofSeconds(2).toMillis();
Expand All @@ -131,15 +140,15 @@ void shouldNeverInvalidateCacheEntryWhenCacheTimeoutZero() throws InterruptedExc
@Test
void shouldUpdateTileAtLocationIfDataChanged() {

cache = new DirectoryBasedCache(CACHE_DIR, Defaults.DEFAULT_CACHE_TIMEOUT);
DirectoryBasedCache cache = new DirectoryBasedCache(TEST_CACHE_DIR, Defaults.DEFAULT_CACHE_TIMEOUT);
cache.put(GENERIC_URL, TEST_IMAGE);
retrieveFromCacheAndCheckContent(GENERIC_URL, TEST_IMAGE);
retrieveFromCacheAndCheckContent(cache, GENERIC_URL, TEST_IMAGE);

cache.put(GENERIC_URL, TEST_IMAGE_OTHER);
retrieveFromCacheAndCheckContent(GENERIC_URL, TEST_IMAGE_OTHER);
retrieveFromCacheAndCheckContent(cache, GENERIC_URL, TEST_IMAGE_OTHER);
}

protected void retrieveFromCacheAndCheckContent(String url, BufferedImage img2) {
protected void retrieveFromCacheAndCheckContent(Cache<String,BufferedImage> cache, String url, BufferedImage img2) {
assertThat(cache.get(url)).isNotNull();
assertThat(compareImages(cache.get(url), img2)).isTrue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@

class DownloadingTileProviderTest {

private final DirectoryBasedCache secondaryCache = new DirectoryBasedCache(CACHE_DIR, Defaults.DEFAULT_CACHE_TIMEOUT);
private MockHttpServer testTileServer;
private static final Path CACHE_DIR = new File("target/testcache").toPath();
private static final File TEST_TILE_FILE = MockHttpServer.TEST_TILE_FILE;
private static final Duration SHORT_TIMEOUT_FOR_VALIDITY_TESTS = Duration.ofSeconds(2);
private final Path TEST_CACHE_DIR = new File("target/testcache").toPath();
private final DirectoryBasedCache secondaryCache = new DirectoryBasedCache(TEST_CACHE_DIR, Defaults.DEFAULT_CACHE_TIMEOUT);
private final File TEST_TILE_FILE = MockHttpServer.TEST_TILE_FILE;
private final Duration SHORT_TIMEOUT_FOR_VALIDITY_TESTS = Duration.ofSeconds(2);

private MockHttpServer testTileServer;

private String tileUrl;
private String failTileUrl;
Expand All @@ -44,13 +45,12 @@ class DownloadingTileProviderTest {

@BeforeEach
void before() throws IOException {
//mapViewer = new MapViewer();
testTileServer = new MockHttpServer();
primaryCache = mapViewer.getPrimaryTileCache();
mapViewer.setSecondaryTileCache(secondaryCache);
tileUrl = "http://localhost:" + testTileServer.getPort() + "/tile.png";
failTileUrl = "http://localhost:" +testTileServer.getPort() +"/nonexisting";
FileUtils.deleteDirectory(CACHE_DIR.toFile());
FileUtils.deleteDirectory(TEST_CACHE_DIR.toFile());
}

@AfterEach
Expand Down Expand Up @@ -81,8 +81,6 @@ void shouldNotDownloadNonExistingResource() {

}



@Test
void shouldNotDownloadTileIfItIsInPrimaryCache() throws IOException {

Expand All @@ -104,7 +102,6 @@ void shouldPutFileInPrimaryCacheIfSecondaryCacheDisabled() {

}


@Test
void shouldNotDownloadTileIfItIsInTheSecondaryCache() throws IOException {

Expand Down

0 comments on commit 3a63b71

Please sign in to comment.