From 9bdff56ed7606c64ad584646d6d97bce5dedb170 Mon Sep 17 00:00:00 2001 From: Oliver Lockwood Date: Sun, 18 Jan 2026 08:58:05 +0000 Subject: [PATCH 1/3] #14: upgrade ehcache from 2.x to 3.x --- pom.xml | 18 ++-- .../simons/oembed/CachedOembedResponse.java | 78 +++++++++++++++ .../CachedOembedResponseExpiryPolicy.java | 66 +++++++++++++ .../java/ac/simons/oembed/OembedService.java | 36 +++++-- .../oembed/CachedOembedResponseTests.java | 94 +++++++++++++++++++ .../ac/simons/oembed/OembedServiceTests.java | 73 ++++++++------ 6 files changed, 324 insertions(+), 41 deletions(-) create mode 100644 src/main/java/ac/simons/oembed/CachedOembedResponse.java create mode 100644 src/main/java/ac/simons/oembed/CachedOembedResponseExpiryPolicy.java create mode 100644 src/test/java/ac/simons/oembed/CachedOembedResponseTests.java diff --git a/pom.xml b/pom.xml index 6f635d7..c2f9bc6 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ 12.3.1 1.11.0 1.3.5 - 2.10.9.2 + 3.11.1 4.5.14 4.4.16 2.20.1 @@ -181,16 +181,22 @@ jakarta.xml.bind-api ${jakarta.xml.bind-api.version} - - net.sf.ehcache - ehcache - ${ehcache.version} - org.apache.httpcomponents httpclient ${httpclient.version} + + org.ehcache + ehcache + ${ehcache.version} + + + org.glassfish.jaxb + jaxb-runtime + + + org.jsoup jsoup diff --git a/src/main/java/ac/simons/oembed/CachedOembedResponse.java b/src/main/java/ac/simons/oembed/CachedOembedResponse.java new file mode 100644 index 0000000..414643f --- /dev/null +++ b/src/main/java/ac/simons/oembed/CachedOembedResponse.java @@ -0,0 +1,78 @@ +/* + * Created by Michael Simons, michael-simons.eu + * and released under The BSD License + * http://www.opensource.org/licenses/bsd-license.php + * + * Copyright (c) 2010-2026, Michael Simons + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of michael-simons.eu nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package ac.simons.oembed; + +import java.io.Serializable; + +/** + * A wrapper class for caching {@link OembedResponse} objects with per-entry TTL support. + * Ehcache 3.x doesn't support per-element TTL like Ehcache 2.x did, so this wrapper + * stores the TTL in seconds which is used by {@link CachedOembedResponseExpiryPolicy}. + * + * @author Michael J. Simons + * @since 2026-01-17 + */ +public final class CachedOembedResponse implements Serializable { + + private static final long serialVersionUID = 1L; + + private final OembedResponse response; + + private final int ttlSeconds; + + /** + * Creates a new cached response wrapper. + * @param response the oembed response, may be null for failed lookups + * @param ttlSeconds the time-to-live in seconds + */ + public CachedOembedResponse(OembedResponse response, int ttlSeconds) { + this.response = response; + this.ttlSeconds = ttlSeconds; + } + + /** + * {@return the wrapped oembed response, may be null} + */ + public OembedResponse getResponse() { + return this.response; + } + + /** + * {@return the time-to-live in seconds for this cache entry} + */ + public int getTtlSeconds() { + return this.ttlSeconds; + } + +} diff --git a/src/main/java/ac/simons/oembed/CachedOembedResponseExpiryPolicy.java b/src/main/java/ac/simons/oembed/CachedOembedResponseExpiryPolicy.java new file mode 100644 index 0000000..a296cb2 --- /dev/null +++ b/src/main/java/ac/simons/oembed/CachedOembedResponseExpiryPolicy.java @@ -0,0 +1,66 @@ +/* + * Created by Michael Simons, michael-simons.eu + * and released under The BSD License + * http://www.opensource.org/licenses/bsd-license.php + * + * Copyright (c) 2010-2026, Michael Simons + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of michael-simons.eu nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package ac.simons.oembed; + +import java.time.Duration; +import java.util.function.Supplier; + +import org.ehcache.expiry.ExpiryPolicy; + +/** + * A custom {@link ExpiryPolicy} for Ehcache 3.x that extracts the TTL from + * {@link CachedOembedResponse} wrappers, enabling per-entry expiration times. + * + * @author Michael J. Simons + * @since 2026-01-17 + */ +public final class CachedOembedResponseExpiryPolicy implements ExpiryPolicy { + + @Override + public Duration getExpiryForCreation(String key, CachedOembedResponse value) { + return Duration.ofSeconds(value.getTtlSeconds()); + } + + @Override + public Duration getExpiryForAccess(String key, Supplier value) { + return null; + } + + @Override + public Duration getExpiryForUpdate(String key, Supplier oldValue, + CachedOembedResponse newValue) { + return Duration.ofSeconds(newValue.getTtlSeconds()); + } + +} diff --git a/src/main/java/ac/simons/oembed/OembedService.java b/src/main/java/ac/simons/oembed/OembedService.java index bb28fa5..7e74c0d 100644 --- a/src/main/java/ac/simons/oembed/OembedService.java +++ b/src/main/java/ac/simons/oembed/OembedService.java @@ -50,14 +50,16 @@ import java.util.stream.Collectors; import ac.simons.oembed.OembedResponse.Format; -import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Ehcache; import org.apache.commons.beanutils.BeanUtils; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.util.EntityUtils; +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -236,7 +238,8 @@ public String getCacheName() { * @param cacheName the new cache name */ public void setCacheName(final String cacheName) { - if (this.cacheManager.isPresent() && this.cacheManager.get().cacheExists(this.cacheName)) { + if (this.cacheManager.isPresent() + && this.cacheManager.get().getCache(this.cacheName, String.class, CachedOembedResponse.class) != null) { this.cacheManager.get().removeCache(this.cacheName); } this.cacheName = cacheName; @@ -331,6 +334,24 @@ final InputStream executeRequest(final HttpGet request) { return rv; } + /** + * Gets or creates the cache for oembed responses. In Ehcache 3.x, caches need to be + * explicitly created with a configuration. + * @param cm the cache manager + * @return the cache instance + */ + private Cache getOrCreateCache(CacheManager cm) { + Cache cache = cm.getCache(this.cacheName, String.class, + CachedOembedResponse.class); + if (cache == null) { + cache = cm.createCache(this.cacheName, CacheConfigurationBuilder + .newCacheConfigurationBuilder(String.class, CachedOembedResponse.class, ResourcePoolsBuilder.heap(1000)) + .withExpiry(new CachedOembedResponseExpiryPolicy()) + .build()); + } + return cache; + } + /** * Tries to find an {@link OembedResponse} for the URL {@code url}. If a cache manager * is present, it tries that first. If an {@code OembedResponse} can be discovered and @@ -345,8 +366,9 @@ public Optional getOembedResponseFor(final String url) { return Optional.empty(); } - var rv = this.cacheManager.map(cm -> cm.addCacheIfAbsent(this.cacheName).get(trimmedUrl)) - .map(element -> (OembedResponse) element.getObjectValue()); + var rv = this.cacheManager.map(this::getOrCreateCache) + .map(cache -> cache.get(trimmedUrl)) + .map(CachedOembedResponse::getResponse); // If there's already an oembed response cached, use that if (rv.isPresent()) { LOGGER.debug("Using OembedResponse from cache for '{}'...", trimmedUrl); @@ -372,13 +394,13 @@ public Optional getOembedResponseFor(final String url) { }); if (this.cacheManager.isPresent()) { - final Ehcache cache = this.cacheManager.get().addCacheIfAbsent(this.cacheName); + final Cache cache = getOrCreateCache(this.cacheManager.get()); // Cache at least 60 seconds final int cacheAge = (int) Math.min( Math.max(60L, rv.map(OembedResponse::getCacheAge).orElse(this.defaultCacheAge)), Integer.MAX_VALUE); // We're adding failed urls to the cache as well to prevent them // from being tried again over and over (at least for some seconds) - cache.put(new net.sf.ehcache.Element(trimmedUrl, rv.orElse(null), cacheAge, cacheAge)); + cache.put(trimmedUrl, new CachedOembedResponse(rv.orElse(null), cacheAge)); LOGGER.debug("Cached {} for {} seconds for url '{}'...", rv, cacheAge, trimmedUrl); } diff --git a/src/test/java/ac/simons/oembed/CachedOembedResponseTests.java b/src/test/java/ac/simons/oembed/CachedOembedResponseTests.java new file mode 100644 index 0000000..1a27fc3 --- /dev/null +++ b/src/test/java/ac/simons/oembed/CachedOembedResponseTests.java @@ -0,0 +1,94 @@ +/* + * Created by Michael Simons, michael-simons.eu + * and released under The BSD License + * http://www.opensource.org/licenses/bsd-license.php + * + * Copyright (c) 2010-2026, Michael Simons + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of michael-simons.eu nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package ac.simons.oembed; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Michael J. Simons + * @since 2026-01-17 + */ +class CachedOembedResponseTests { + + @Test + void shouldStoreTtlAndResponse() { + OembedResponse response = new OembedResponse(); + response.setType("rich"); + response.setVersion("1.0"); + + CachedOembedResponse cached = new CachedOembedResponse(response, 3600); + + assertThat(cached.getResponse()).isSameAs(response); + assertThat(cached.getTtlSeconds()).isEqualTo(3600); + } + + @Test + void shouldHandleNullResponse() { + CachedOembedResponse cached = new CachedOembedResponse(null, 60); + + assertThat(cached.getResponse()).isNull(); + assertThat(cached.getTtlSeconds()).isEqualTo(60); + } + + @Test + void expiryPolicyShouldReturnCorrectDuration() { + CachedOembedResponseExpiryPolicy policy = new CachedOembedResponseExpiryPolicy(); + CachedOembedResponse cached = new CachedOembedResponse(null, 120); + + Duration duration = policy.getExpiryForCreation("key", cached); + assertThat(duration).isEqualTo(Duration.ofSeconds(120)); + } + + @Test + void expiryPolicyShouldReturnNullForAccess() { + CachedOembedResponseExpiryPolicy policy = new CachedOembedResponseExpiryPolicy(); + + Duration duration = policy.getExpiryForAccess("key", () -> new CachedOembedResponse(null, 120)); + assertThat(duration).isNull(); + } + + @Test + void expiryPolicyShouldReturnDurationForUpdate() { + CachedOembedResponseExpiryPolicy policy = new CachedOembedResponseExpiryPolicy(); + CachedOembedResponse newValue = new CachedOembedResponse(null, 300); + + Duration duration = policy.getExpiryForUpdate("key", () -> new CachedOembedResponse(null, 120), newValue); + assertThat(duration).isEqualTo(Duration.ofSeconds(300)); + } + +} diff --git a/src/test/java/ac/simons/oembed/OembedServiceTests.java b/src/test/java/ac/simons/oembed/OembedServiceTests.java index 26c862a..d9ea746 100644 --- a/src/test/java/ac/simons/oembed/OembedServiceTests.java +++ b/src/test/java/ac/simons/oembed/OembedServiceTests.java @@ -42,12 +42,11 @@ import java.util.Optional; import ac.simons.oembed.OembedResponse.Format; -import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Ehcache; -import net.sf.ehcache.Element; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; +import org.ehcache.Cache; +import org.ehcache.CacheManager; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -248,9 +247,12 @@ public void executeRequestShouldWork3() throws IOException { } @Test + @SuppressWarnings("unchecked") public void setCacheNameShouldWork() { - given(this.cacheManager.cacheExists("ac.simons.oembed.OembedService")).willReturn(false); - given(this.cacheManager.cacheExists("x")).willReturn(true); + Cache existingCache = Mockito.mock(Cache.class); + given(this.cacheManager.getCache("ac.simons.oembed.OembedService", String.class, CachedOembedResponse.class)) + .willReturn(null); + given(this.cacheManager.getCache("x", String.class, CachedOembedResponse.class)).willReturn(existingCache); OembedService oembedService; oembedService = new OembedService(this.defaultHttpClient, null, new ArrayList<>(), null); @@ -264,8 +266,8 @@ public void setCacheNameShouldWork() { oembedService.setCacheName("y"); assertThat(oembedService.getCacheName()).isEqualTo("y"); - verify(this.cacheManager).cacheExists(OembedService.class.getName()); - verify(this.cacheManager).cacheExists("x"); + verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class); + verify(this.cacheManager).getCache("x", String.class, CachedOembedResponse.class); verify(this.cacheManager).removeCache("x"); Mockito.verifyNoMoreInteractions(this.cacheManager); } @@ -282,11 +284,14 @@ public void getOembedResponseForShouldWork1() { * Response from cache */ @Test + @SuppressWarnings("unchecked") public void getOembedResponseForShouldWork2() { - Ehcache cache = Mockito.mock(Ehcache.class); + Cache cache = Mockito.mock(Cache.class); String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; - given(cache.get(embeddableUrl)).willReturn(new Element(embeddableUrl, this.response1)); - given(this.cacheManager.addCacheIfAbsent("testCache")).willReturn(cache); + given(cache.get(embeddableUrl)).willReturn(new CachedOembedResponse(this.response1, 86400)); + given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + .willReturn(null); OembedService oembedService = new OembedService(this.defaultHttpClient, this.cacheManager, new ArrayList<>(), null); @@ -308,8 +313,8 @@ public void getOembedResponseForShouldWork2() { assertThat(response.getVersion()).isEqualTo("1.0"); assertThat(oembedService.getCacheName()).isEqualTo("testCache"); - verify(this.cacheManager).addCacheIfAbsent("testCache"); - verify(this.cacheManager).cacheExists(OembedService.class.getName()); + verify(this.cacheManager).getCache("testCache", String.class, CachedOembedResponse.class); + verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class); verify(cache).get(embeddableUrl); Mockito.verifyNoMoreInteractions(cache, this.cacheManager); Mockito.verifyNoInteractions(this.defaultHttpClient); @@ -320,6 +325,7 @@ public void getOembedResponseForShouldWork2() { * @throws IOException all of them */ @Test + @SuppressWarnings("unchecked") public void getOembedResponseForShouldWork3() throws IOException { String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; @@ -337,9 +343,11 @@ public void getOembedResponseForShouldWork3() throws IOException { given(this.defaultHttpClient.execute(any(HttpGet.class))).willReturn(r); - Ehcache cache = Mockito.mock(Ehcache.class); + Cache cache = Mockito.mock(Cache.class); given(cache.get(embeddableUrl)).willReturn(null); - given(this.cacheManager.addCacheIfAbsent("testCache")).willReturn(cache); + given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + .willReturn(null); OembedService oembedService = new OembedService(this.defaultHttpClient, this.cacheManager, List.of(oembedEndpoint), null); @@ -350,10 +358,10 @@ public void getOembedResponseForShouldWork3() throws IOException { assertThat(argumentCaptor.getValue().getURI()).hasToString( "https://biking.michael-simons.eu/oembed?format=json&url=https%3A%2F%2Fbiking.michael-simons.eu%2Ftracks%2F1&maxwidth=480&maxheight=360"); - verify(this.cacheManager, times(2)).addCacheIfAbsent("testCache"); - verify(this.cacheManager).cacheExists(OembedService.class.getName()); + verify(this.cacheManager, times(2)).getCache("testCache", String.class, CachedOembedResponse.class); + verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class); verify(cache).get(embeddableUrl); - verify(cache).put(any(Element.class)); + verify(cache).put(any(String.class), any(CachedOembedResponse.class)); verifyNoMoreInteractions(cache, this.cacheManager, this.defaultHttpClient); } @@ -363,6 +371,7 @@ public void getOembedResponseForShouldWork3() throws IOException { * @throws IOException all of them */ @Test + @SuppressWarnings("unchecked") public void getOembedResponseForShouldWork4() throws IOException { String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; @@ -380,9 +389,11 @@ public void getOembedResponseForShouldWork4() throws IOException { given(this.defaultHttpClient.execute(any(HttpGet.class))).willReturn(r); - Ehcache cache = Mockito.mock(Ehcache.class); + Cache cache = Mockito.mock(Cache.class); given(cache.get(embeddableUrl)).willReturn(null); - given(this.cacheManager.addCacheIfAbsent("testCache")).willReturn(cache); + given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + .willReturn(null); OembedService oembedService = new OembedService(this.defaultHttpClient, this.cacheManager, List.of(oembedEndpoint), null); @@ -393,10 +404,10 @@ public void getOembedResponseForShouldWork4() throws IOException { assertThat(argumentCaptor.getValue().getURI()).hasToString( "https://biking.michael-simons.eu/oembed?format=json&url=https%3A%2F%2Fbiking.michael-simons.eu%2Ftracks%2F1&maxwidth=480&maxheight=360"); - verify(this.cacheManager, times(2)).addCacheIfAbsent("testCache"); - verify(this.cacheManager).cacheExists(OembedService.class.getName()); + verify(this.cacheManager, times(2)).getCache("testCache", String.class, CachedOembedResponse.class); + verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class); verify(cache).get(embeddableUrl); - verify(cache).put(any(Element.class)); + verify(cache).put(any(String.class), any(CachedOembedResponse.class)); verifyNoMoreInteractions(cache, this.cacheManager, this.defaultHttpClient); } @@ -454,11 +465,14 @@ public void embedUrlsShouldWork1() { * configured, also no renders. */ @Test + @SuppressWarnings("unchecked") public void embedUrlsShouldWork2() { - Ehcache cache = Mockito.mock(Ehcache.class); + Cache cache = Mockito.mock(Cache.class); String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; - given(cache.get(embeddableUrl)).willReturn(new Element(embeddableUrl, this.response1)); - given(this.cacheManager.addCacheIfAbsent("testCache")).willReturn(cache); + given(cache.get(embeddableUrl)).willReturn(new CachedOembedResponse(this.response1, 86400)); + given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + .willReturn(null); OembedService oembedService = new OembedService(this.defaultHttpClient, this.cacheManager, new ArrayList<>(), null); @@ -471,11 +485,14 @@ public void embedUrlsShouldWork2() { } @Test + @SuppressWarnings("unchecked") public void embedUrlsShouldWork3() { - Ehcache cache = Mockito.mock(Ehcache.class); + Cache cache = Mockito.mock(Cache.class); String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; - given(cache.get(embeddableUrl)).willReturn(new Element(embeddableUrl, this.response1)); - given(this.cacheManager.addCacheIfAbsent("testCache")).willReturn(cache); + given(cache.get(embeddableUrl)).willReturn(new CachedOembedResponse(this.response1, 86400)); + given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + .willReturn(null); OembedEndpoint oembedEndpoint = new OembedEndpoint(); oembedEndpoint.setName("biking"); From 9be6eec0cbade0ac64e08c73e40520138e201169 Mon Sep 17 00:00:00 2001 From: Oliver Lockwood Date: Sun, 18 Jan 2026 09:02:33 +0000 Subject: [PATCH 2/3] Fix warning suppression --- .../ac/simons/oembed/OembedServiceTests.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/test/java/ac/simons/oembed/OembedServiceTests.java b/src/test/java/ac/simons/oembed/OembedServiceTests.java index d9ea746..da05546 100644 --- a/src/test/java/ac/simons/oembed/OembedServiceTests.java +++ b/src/test/java/ac/simons/oembed/OembedServiceTests.java @@ -76,6 +76,9 @@ public class OembedServiceTests { @Mock private CacheManager cacheManager; + @Mock + private Cache cache; + private final String responseString = "{\"author_name\":\"Michael J. Simons\",\"author_url\":\"http://michael-simons.eu\",\"cache_age\":86400,\"html\":\"\",\"provider_name\":\"biking2\",\"provider_url\":\"https://biking.michael-simons.eu\",\"title\":\"Aachen - Maastricht - Aachen\",\"type\":\"rich\",\"version\":\"1.0\"}"; private final OembedResponse response1; @@ -247,12 +250,10 @@ public void executeRequestShouldWork3() throws IOException { } @Test - @SuppressWarnings("unchecked") public void setCacheNameShouldWork() { - Cache existingCache = Mockito.mock(Cache.class); given(this.cacheManager.getCache("ac.simons.oembed.OembedService", String.class, CachedOembedResponse.class)) .willReturn(null); - given(this.cacheManager.getCache("x", String.class, CachedOembedResponse.class)).willReturn(existingCache); + given(this.cacheManager.getCache("x", String.class, CachedOembedResponse.class)).willReturn(cache); OembedService oembedService; oembedService = new OembedService(this.defaultHttpClient, null, new ArrayList<>(), null); @@ -284,9 +285,7 @@ public void getOembedResponseForShouldWork1() { * Response from cache */ @Test - @SuppressWarnings("unchecked") public void getOembedResponseForShouldWork2() { - Cache cache = Mockito.mock(Cache.class); String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; given(cache.get(embeddableUrl)).willReturn(new CachedOembedResponse(this.response1, 86400)); given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); @@ -325,7 +324,6 @@ public void getOembedResponseForShouldWork2() { * @throws IOException all of them */ @Test - @SuppressWarnings("unchecked") public void getOembedResponseForShouldWork3() throws IOException { String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; @@ -343,7 +341,6 @@ public void getOembedResponseForShouldWork3() throws IOException { given(this.defaultHttpClient.execute(any(HttpGet.class))).willReturn(r); - Cache cache = Mockito.mock(Cache.class); given(cache.get(embeddableUrl)).willReturn(null); given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) @@ -371,7 +368,6 @@ public void getOembedResponseForShouldWork3() throws IOException { * @throws IOException all of them */ @Test - @SuppressWarnings("unchecked") public void getOembedResponseForShouldWork4() throws IOException { String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; @@ -389,7 +385,6 @@ public void getOembedResponseForShouldWork4() throws IOException { given(this.defaultHttpClient.execute(any(HttpGet.class))).willReturn(r); - Cache cache = Mockito.mock(Cache.class); given(cache.get(embeddableUrl)).willReturn(null); given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) @@ -465,9 +460,7 @@ public void embedUrlsShouldWork1() { * configured, also no renders. */ @Test - @SuppressWarnings("unchecked") public void embedUrlsShouldWork2() { - Cache cache = Mockito.mock(Cache.class); String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; given(cache.get(embeddableUrl)).willReturn(new CachedOembedResponse(this.response1, 86400)); given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); @@ -485,9 +478,7 @@ public void embedUrlsShouldWork2() { } @Test - @SuppressWarnings("unchecked") public void embedUrlsShouldWork3() { - Cache cache = Mockito.mock(Cache.class); String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; given(cache.get(embeddableUrl)).willReturn(new CachedOembedResponse(this.response1, 86400)); given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); From 02e7dd7891a609fe1374042a73a263fda881142f Mon Sep 17 00:00:00 2001 From: "Michael J. Simons" Date: Sun, 18 Jan 2026 17:44:41 +0100 Subject: [PATCH 3/3] Refactor a bit. --- ...e.java => OembedResponseExpiryPolicy.java} | 66 ++++++++++------- ...Policy.java => OembedResponseWrapper.java} | 29 +------- .../java/ac/simons/oembed/OembedService.java | 60 +++++++-------- ...a => OembedResponseExpiryPolicyTests.java} | 47 ++++++------ .../ac/simons/oembed/OembedServiceTests.java | 73 ++++++++++--------- 5 files changed, 132 insertions(+), 143 deletions(-) rename src/main/java/ac/simons/oembed/{CachedOembedResponse.java => OembedResponseExpiryPolicy.java} (56%) rename src/main/java/ac/simons/oembed/{CachedOembedResponseExpiryPolicy.java => OembedResponseWrapper.java} (68%) rename src/test/java/ac/simons/oembed/{CachedOembedResponseTests.java => OembedResponseExpiryPolicyTests.java} (73%) diff --git a/src/main/java/ac/simons/oembed/CachedOembedResponse.java b/src/main/java/ac/simons/oembed/OembedResponseExpiryPolicy.java similarity index 56% rename from src/main/java/ac/simons/oembed/CachedOembedResponse.java rename to src/main/java/ac/simons/oembed/OembedResponseExpiryPolicy.java index 414643f..6db88c0 100644 --- a/src/main/java/ac/simons/oembed/CachedOembedResponse.java +++ b/src/main/java/ac/simons/oembed/OembedResponseExpiryPolicy.java @@ -33,46 +33,60 @@ */ package ac.simons.oembed; -import java.io.Serializable; +import java.time.Duration; +import java.util.function.Supplier; + +import org.ehcache.expiry.ExpiryPolicy; /** - * A wrapper class for caching {@link OembedResponse} objects with per-entry TTL support. - * Ehcache 3.x doesn't support per-element TTL like Ehcache 2.x did, so this wrapper - * stores the TTL in seconds which is used by {@link CachedOembedResponseExpiryPolicy}. + * A custom {@link ExpiryPolicy} for Ehcache 3.x that extracts the TTL from an + * {@link OembedResponse}, enabling per-entry expiration times. * + * @author Oliver Lockwood * @author Michael J. Simons * @since 2026-01-17 */ -public final class CachedOembedResponse implements Serializable { +final class OembedResponseExpiryPolicy implements ExpiryPolicy { - private static final long serialVersionUID = 1L; + /** + * Time in seconds responses are cached. Used if the response has no cache_age. + */ + private long defaultCacheAge = 3600; - private final OembedResponse response; + @Override + public Duration getExpiryForCreation(String key, OembedResponseWrapper value) { + return getExpiryOf(value); + } - private final int ttlSeconds; + @Override + public Duration getExpiryForAccess(String key, Supplier value) { + return null; + } - /** - * Creates a new cached response wrapper. - * @param response the oembed response, may be null for failed lookups - * @param ttlSeconds the time-to-live in seconds - */ - public CachedOembedResponse(OembedResponse response, int ttlSeconds) { - this.response = response; - this.ttlSeconds = ttlSeconds; + @Override + public Duration getExpiryForUpdate(String key, Supplier oldValue, + OembedResponseWrapper newValue) { + return getExpiryOf(newValue); } - /** - * {@return the wrapped oembed response, may be null} - */ - public OembedResponse getResponse() { - return this.response; + Duration getExpiryOf(OembedResponseWrapper wrapper) { + long cacheAge; + if (wrapper.value() != null && wrapper.value().getCacheAge() != null) { + // Cache at least 60 seconds + cacheAge = Math.max(60, wrapper.value().getCacheAge()); + } + else { + cacheAge = this.defaultCacheAge; + } + return Duration.ofSeconds(Math.min(cacheAge, Integer.MAX_VALUE)); } - /** - * {@return the time-to-live in seconds for this cache entry} - */ - public int getTtlSeconds() { - return this.ttlSeconds; + long getDefaultCacheAge() { + return this.defaultCacheAge; + } + + void setDefaultCacheAge(long defaultCacheAge) { + this.defaultCacheAge = defaultCacheAge; } } diff --git a/src/main/java/ac/simons/oembed/CachedOembedResponseExpiryPolicy.java b/src/main/java/ac/simons/oembed/OembedResponseWrapper.java similarity index 68% rename from src/main/java/ac/simons/oembed/CachedOembedResponseExpiryPolicy.java rename to src/main/java/ac/simons/oembed/OembedResponseWrapper.java index a296cb2..670aab6 100644 --- a/src/main/java/ac/simons/oembed/CachedOembedResponseExpiryPolicy.java +++ b/src/main/java/ac/simons/oembed/OembedResponseWrapper.java @@ -33,34 +33,13 @@ */ package ac.simons.oembed; -import java.time.Duration; -import java.util.function.Supplier; - -import org.ehcache.expiry.ExpiryPolicy; - /** - * A custom {@link ExpiryPolicy} for Ehcache 3.x that extracts the TTL from - * {@link CachedOembedResponse} wrappers, enabling per-entry expiration times. + * EHCache 3 is unhappy with {@literal null} values, so we wrap them. * + * @param value actual OEmbed response, might be {@literal null} + * @author Oliver Lockwood * @author Michael J. Simons * @since 2026-01-17 */ -public final class CachedOembedResponseExpiryPolicy implements ExpiryPolicy { - - @Override - public Duration getExpiryForCreation(String key, CachedOembedResponse value) { - return Duration.ofSeconds(value.getTtlSeconds()); - } - - @Override - public Duration getExpiryForAccess(String key, Supplier value) { - return null; - } - - @Override - public Duration getExpiryForUpdate(String key, Supplier oldValue, - CachedOembedResponse newValue) { - return Duration.ofSeconds(newValue.getTtlSeconds()); - } - +record OembedResponseWrapper(OembedResponse value) { } diff --git a/src/main/java/ac/simons/oembed/OembedService.java b/src/main/java/ac/simons/oembed/OembedService.java index 7e74c0d..004e701 100644 --- a/src/main/java/ac/simons/oembed/OembedService.java +++ b/src/main/java/ac/simons/oembed/OembedService.java @@ -86,7 +86,7 @@ public class OembedService { /** * An optional cache manager used for caching oembed responses. */ - private final Optional cacheManager; + private final CacheManager cacheManager; /** * The user agent to use. We want to be a goot net citizen and provide some info about @@ -125,11 +125,6 @@ public class OembedService { */ private String cacheName = OembedService.class.getName(); - /** - * Time in seconds responses are cached. Used if the response has no cache_age. - */ - private long defaultCacheAge = 3600; - /** * Used for auto-discovered endpoints. */ @@ -140,6 +135,8 @@ public class OembedService { */ private final OembedResponseRenderer defaultRenderer = new DefaultOembedResponseRenderer(); + private final OembedResponseExpiryPolicy expiryPolicy = new OembedResponseExpiryPolicy(); + /** * Creates a new {@code OembedService}. This service depends on a {@link HttpClient} * and can use a {@link CacheManager} for caching requests. @@ -151,7 +148,7 @@ public class OembedService { public OembedService(final HttpClient httpClient, final CacheManager cacheManager, final List endpoints, final String applicationName) { this.httpClient = httpClient; - this.cacheManager = Optional.ofNullable(cacheManager); + this.cacheManager = cacheManager; final Properties version = new Properties(); try { version.load(OembedService.class.getResourceAsStream("/oembed.properties")); @@ -238,9 +235,9 @@ public String getCacheName() { * @param cacheName the new cache name */ public void setCacheName(final String cacheName) { - if (this.cacheManager.isPresent() - && this.cacheManager.get().getCache(this.cacheName, String.class, CachedOembedResponse.class) != null) { - this.cacheManager.get().removeCache(this.cacheName); + if (this.cacheManager != null + && this.cacheManager.getCache(this.cacheName, String.class, OembedResponseWrapper.class) != null) { + this.cacheManager.removeCache(this.cacheName); } this.cacheName = cacheName; } @@ -249,7 +246,7 @@ public void setCacheName(final String cacheName) { * {@return the default time in seconds responses are cached} */ public long getDefaultCacheAge() { - return this.defaultCacheAge; + return this.expiryPolicy.getDefaultCacheAge(); } /** @@ -257,7 +254,7 @@ public long getDefaultCacheAge() { * @param defaultCacheAge new default cache age in seconds */ public void setDefaultCacheAge(final long defaultCacheAge) { - this.defaultCacheAge = defaultCacheAge; + this.expiryPolicy.setDefaultCacheAge(defaultCacheAge); } /** @@ -337,19 +334,21 @@ final InputStream executeRequest(final HttpGet request) { /** * Gets or creates the cache for oembed responses. In Ehcache 3.x, caches need to be * explicitly created with a configuration. - * @param cm the cache manager - * @return the cache instance + * @return the cache instance or null if there is no cache manager */ - private Cache getOrCreateCache(CacheManager cm) { - Cache cache = cm.getCache(this.cacheName, String.class, - CachedOembedResponse.class); - if (cache == null) { - cache = cm.createCache(this.cacheName, CacheConfigurationBuilder - .newCacheConfigurationBuilder(String.class, CachedOembedResponse.class, ResourcePoolsBuilder.heap(1000)) - .withExpiry(new CachedOembedResponseExpiryPolicy()) - .build()); + private Cache getOrCreateCache() { + if (this.cacheManager == null) { + return null; + } + var cache = this.cacheManager.getCache(this.cacheName, String.class, OembedResponseWrapper.class); + if (cache != null) { + return cache; } - return cache; + + return this.cacheManager.createCache(this.cacheName, CacheConfigurationBuilder + .newCacheConfigurationBuilder(String.class, OembedResponseWrapper.class, ResourcePoolsBuilder.heap(1000)) + .withExpiry(new OembedResponseExpiryPolicy()) + .build()); } /** @@ -366,9 +365,9 @@ public Optional getOembedResponseFor(final String url) { return Optional.empty(); } - var rv = this.cacheManager.map(this::getOrCreateCache) + var rv = Optional.ofNullable(this.getOrCreateCache()) .map(cache -> cache.get(trimmedUrl)) - .map(CachedOembedResponse::getResponse); + .map(OembedResponseWrapper::value); // If there's already an oembed response cached, use that if (rv.isPresent()) { LOGGER.debug("Using OembedResponse from cache for '{}'...", trimmedUrl); @@ -393,15 +392,12 @@ public Optional getOembedResponseFor(final String url) { return oembedResponse; }); - if (this.cacheManager.isPresent()) { - final Cache cache = getOrCreateCache(this.cacheManager.get()); - // Cache at least 60 seconds - final int cacheAge = (int) Math.min( - Math.max(60L, rv.map(OembedResponse::getCacheAge).orElse(this.defaultCacheAge)), Integer.MAX_VALUE); + if (this.cacheManager != null) { + var cache = getOrCreateCache(); // We're adding failed urls to the cache as well to prevent them // from being tried again over and over (at least for some seconds) - cache.put(trimmedUrl, new CachedOembedResponse(rv.orElse(null), cacheAge)); - LOGGER.debug("Cached {} for {} seconds for url '{}'...", rv, cacheAge, trimmedUrl); + cache.put(trimmedUrl, new OembedResponseWrapper(rv.orElse(null))); + LOGGER.debug("Cached response {} from url '{}'...", rv, trimmedUrl); } return rv; diff --git a/src/test/java/ac/simons/oembed/CachedOembedResponseTests.java b/src/test/java/ac/simons/oembed/OembedResponseExpiryPolicyTests.java similarity index 73% rename from src/test/java/ac/simons/oembed/CachedOembedResponseTests.java rename to src/test/java/ac/simons/oembed/OembedResponseExpiryPolicyTests.java index 1a27fc3..bb90454 100644 --- a/src/test/java/ac/simons/oembed/CachedOembedResponseTests.java +++ b/src/test/java/ac/simons/oembed/OembedResponseExpiryPolicyTests.java @@ -40,35 +40,21 @@ import static org.assertj.core.api.Assertions.assertThat; /** + * @author Oliver Lockwood * @author Michael J. Simons * @since 2026-01-17 */ -class CachedOembedResponseTests { +class OembedResponseExpiryPolicyTests { @Test - void shouldStoreTtlAndResponse() { + void expiryPolicyShouldReturnCorrectDuration() { OembedResponse response = new OembedResponse(); response.setType("rich"); response.setVersion("1.0"); + response.setCacheAge(120L); - CachedOembedResponse cached = new CachedOembedResponse(response, 3600); - - assertThat(cached.getResponse()).isSameAs(response); - assertThat(cached.getTtlSeconds()).isEqualTo(3600); - } - - @Test - void shouldHandleNullResponse() { - CachedOembedResponse cached = new CachedOembedResponse(null, 60); - - assertThat(cached.getResponse()).isNull(); - assertThat(cached.getTtlSeconds()).isEqualTo(60); - } - - @Test - void expiryPolicyShouldReturnCorrectDuration() { - CachedOembedResponseExpiryPolicy policy = new CachedOembedResponseExpiryPolicy(); - CachedOembedResponse cached = new CachedOembedResponse(null, 120); + OembedResponseExpiryPolicy policy = new OembedResponseExpiryPolicy(); + OembedResponseWrapper cached = new OembedResponseWrapper(response); Duration duration = policy.getExpiryForCreation("key", cached); assertThat(duration).isEqualTo(Duration.ofSeconds(120)); @@ -76,18 +62,27 @@ void expiryPolicyShouldReturnCorrectDuration() { @Test void expiryPolicyShouldReturnNullForAccess() { - CachedOembedResponseExpiryPolicy policy = new CachedOembedResponseExpiryPolicy(); + OembedResponseExpiryPolicy policy = new OembedResponseExpiryPolicy(); - Duration duration = policy.getExpiryForAccess("key", () -> new CachedOembedResponse(null, 120)); + Duration duration = policy.getExpiryForAccess("key", () -> new OembedResponseWrapper(null)); assertThat(duration).isNull(); } @Test void expiryPolicyShouldReturnDurationForUpdate() { - CachedOembedResponseExpiryPolicy policy = new CachedOembedResponseExpiryPolicy(); - CachedOembedResponse newValue = new CachedOembedResponse(null, 300); - - Duration duration = policy.getExpiryForUpdate("key", () -> new CachedOembedResponse(null, 120), newValue); + OembedResponse oldValue = new OembedResponse(); + oldValue.setType("rich"); + oldValue.setVersion("1.0"); + oldValue.setCacheAge(120L); + + OembedResponse newValue = new OembedResponse(); + newValue.setType("rich"); + newValue.setVersion("1.0"); + newValue.setCacheAge(300L); + + OembedResponseExpiryPolicy policy = new OembedResponseExpiryPolicy(); + Duration duration = policy.getExpiryForUpdate("key", () -> new OembedResponseWrapper(oldValue), + new OembedResponseWrapper(newValue)); assertThat(duration).isEqualTo(Duration.ofSeconds(300)); } diff --git a/src/test/java/ac/simons/oembed/OembedServiceTests.java b/src/test/java/ac/simons/oembed/OembedServiceTests.java index da05546..2aa54cd 100644 --- a/src/test/java/ac/simons/oembed/OembedServiceTests.java +++ b/src/test/java/ac/simons/oembed/OembedServiceTests.java @@ -77,7 +77,7 @@ public class OembedServiceTests { private CacheManager cacheManager; @Mock - private Cache cache; + private Cache cache; private final String responseString = "{\"author_name\":\"Michael J. Simons\",\"author_url\":\"http://michael-simons.eu\",\"cache_age\":86400,\"html\":\"\",\"provider_name\":\"biking2\",\"provider_url\":\"https://biking.michael-simons.eu\",\"title\":\"Aachen - Maastricht - Aachen\",\"type\":\"rich\",\"version\":\"1.0\"}"; @@ -251,9 +251,9 @@ public void executeRequestShouldWork3() throws IOException { @Test public void setCacheNameShouldWork() { - given(this.cacheManager.getCache("ac.simons.oembed.OembedService", String.class, CachedOembedResponse.class)) + given(this.cacheManager.getCache("ac.simons.oembed.OembedService", String.class, OembedResponseWrapper.class)) .willReturn(null); - given(this.cacheManager.getCache("x", String.class, CachedOembedResponse.class)).willReturn(cache); + given(this.cacheManager.getCache("x", String.class, OembedResponseWrapper.class)).willReturn(this.cache); OembedService oembedService; oembedService = new OembedService(this.defaultHttpClient, null, new ArrayList<>(), null); @@ -267,8 +267,8 @@ public void setCacheNameShouldWork() { oembedService.setCacheName("y"); assertThat(oembedService.getCacheName()).isEqualTo("y"); - verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class); - verify(this.cacheManager).getCache("x", String.class, CachedOembedResponse.class); + verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, OembedResponseWrapper.class); + verify(this.cacheManager).getCache("x", String.class, OembedResponseWrapper.class); verify(this.cacheManager).removeCache("x"); Mockito.verifyNoMoreInteractions(this.cacheManager); } @@ -287,9 +287,10 @@ public void getOembedResponseForShouldWork1() { @Test public void getOembedResponseForShouldWork2() { String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; - given(cache.get(embeddableUrl)).willReturn(new CachedOembedResponse(this.response1, 86400)); - given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); - given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + given(this.cache.get(embeddableUrl)).willReturn(new OembedResponseWrapper(this.response1)); + given(this.cacheManager.getCache("testCache", String.class, OembedResponseWrapper.class)) + .willReturn(this.cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, OembedResponseWrapper.class)) .willReturn(null); OembedService oembedService = new OembedService(this.defaultHttpClient, this.cacheManager, new ArrayList<>(), @@ -312,10 +313,10 @@ public void getOembedResponseForShouldWork2() { assertThat(response.getVersion()).isEqualTo("1.0"); assertThat(oembedService.getCacheName()).isEqualTo("testCache"); - verify(this.cacheManager).getCache("testCache", String.class, CachedOembedResponse.class); - verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class); - verify(cache).get(embeddableUrl); - Mockito.verifyNoMoreInteractions(cache, this.cacheManager); + verify(this.cacheManager).getCache("testCache", String.class, OembedResponseWrapper.class); + verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, OembedResponseWrapper.class); + verify(this.cache).get(embeddableUrl); + Mockito.verifyNoMoreInteractions(this.cache, this.cacheManager); Mockito.verifyNoInteractions(this.defaultHttpClient); } @@ -341,9 +342,10 @@ public void getOembedResponseForShouldWork3() throws IOException { given(this.defaultHttpClient.execute(any(HttpGet.class))).willReturn(r); - given(cache.get(embeddableUrl)).willReturn(null); - given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); - given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + given(this.cache.get(embeddableUrl)).willReturn(null); + given(this.cacheManager.getCache("testCache", String.class, OembedResponseWrapper.class)) + .willReturn(this.cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, OembedResponseWrapper.class)) .willReturn(null); OembedService oembedService = new OembedService(this.defaultHttpClient, this.cacheManager, @@ -355,12 +357,12 @@ public void getOembedResponseForShouldWork3() throws IOException { assertThat(argumentCaptor.getValue().getURI()).hasToString( "https://biking.michael-simons.eu/oembed?format=json&url=https%3A%2F%2Fbiking.michael-simons.eu%2Ftracks%2F1&maxwidth=480&maxheight=360"); - verify(this.cacheManager, times(2)).getCache("testCache", String.class, CachedOembedResponse.class); - verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class); - verify(cache).get(embeddableUrl); - verify(cache).put(any(String.class), any(CachedOembedResponse.class)); + verify(this.cacheManager, times(2)).getCache("testCache", String.class, OembedResponseWrapper.class); + verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, OembedResponseWrapper.class); + verify(this.cache).get(embeddableUrl); + verify(this.cache).put(any(String.class), any(OembedResponseWrapper.class)); - verifyNoMoreInteractions(cache, this.cacheManager, this.defaultHttpClient); + verifyNoMoreInteractions(this.cache, this.cacheManager, this.defaultHttpClient); } /** @@ -385,9 +387,10 @@ public void getOembedResponseForShouldWork4() throws IOException { given(this.defaultHttpClient.execute(any(HttpGet.class))).willReturn(r); - given(cache.get(embeddableUrl)).willReturn(null); - given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); - given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + given(this.cache.get(embeddableUrl)).willReturn(null); + given(this.cacheManager.getCache("testCache", String.class, OembedResponseWrapper.class)) + .willReturn(this.cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, OembedResponseWrapper.class)) .willReturn(null); OembedService oembedService = new OembedService(this.defaultHttpClient, this.cacheManager, @@ -399,12 +402,12 @@ public void getOembedResponseForShouldWork4() throws IOException { assertThat(argumentCaptor.getValue().getURI()).hasToString( "https://biking.michael-simons.eu/oembed?format=json&url=https%3A%2F%2Fbiking.michael-simons.eu%2Ftracks%2F1&maxwidth=480&maxheight=360"); - verify(this.cacheManager, times(2)).getCache("testCache", String.class, CachedOembedResponse.class); - verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class); - verify(cache).get(embeddableUrl); - verify(cache).put(any(String.class), any(CachedOembedResponse.class)); + verify(this.cacheManager, times(2)).getCache("testCache", String.class, OembedResponseWrapper.class); + verify(this.cacheManager).getCache(OembedService.class.getName(), String.class, OembedResponseWrapper.class); + verify(this.cache).get(embeddableUrl); + verify(this.cache).put(any(String.class), any(OembedResponseWrapper.class)); - verifyNoMoreInteractions(cache, this.cacheManager, this.defaultHttpClient); + verifyNoMoreInteractions(this.cache, this.cacheManager, this.defaultHttpClient); } /** @@ -462,9 +465,10 @@ public void embedUrlsShouldWork1() { @Test public void embedUrlsShouldWork2() { String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; - given(cache.get(embeddableUrl)).willReturn(new CachedOembedResponse(this.response1, 86400)); - given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); - given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + given(this.cache.get(embeddableUrl)).willReturn(new OembedResponseWrapper(this.response1)); + given(this.cacheManager.getCache("testCache", String.class, OembedResponseWrapper.class)) + .willReturn(this.cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, OembedResponseWrapper.class)) .willReturn(null); OembedService oembedService = new OembedService(this.defaultHttpClient, this.cacheManager, new ArrayList<>(), @@ -480,9 +484,10 @@ public void embedUrlsShouldWork2() { @Test public void embedUrlsShouldWork3() { String embeddableUrl = "https://biking.michael-simons.eu/tracks/1"; - given(cache.get(embeddableUrl)).willReturn(new CachedOembedResponse(this.response1, 86400)); - given(this.cacheManager.getCache("testCache", String.class, CachedOembedResponse.class)).willReturn(cache); - given(this.cacheManager.getCache(OembedService.class.getName(), String.class, CachedOembedResponse.class)) + given(this.cache.get(embeddableUrl)).willReturn(new OembedResponseWrapper(this.response1)); + given(this.cacheManager.getCache("testCache", String.class, OembedResponseWrapper.class)) + .willReturn(this.cache); + given(this.cacheManager.getCache(OembedService.class.getName(), String.class, OembedResponseWrapper.class)) .willReturn(null); OembedEndpoint oembedEndpoint = new OembedEndpoint();