diff --git a/build.gradle b/build.gradle index ade9039..6202e22 100644 --- a/build.gradle +++ b/build.gradle @@ -62,7 +62,7 @@ repositories { dependencies { implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version:'2.15.0' - implementation group: 'com.google.guava', name: 'guava', version:'31.1-jre' + implementation group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '2.9.3' testImplementation group: 'junit', name: 'junit', version:'4.13.1' testImplementation group: 'org.mockito', name: 'mockito-core', version:'1.10.19' testImplementation group: 'org.hamcrest', name: 'hamcrest-library', version:'1.3' diff --git a/src/main/java/com/auth0/jwk/GuavaCachedJwkProvider.java b/src/main/java/com/auth0/jwk/CaffeineCachedJwkProvider.java similarity index 61% rename from src/main/java/com/auth0/jwk/GuavaCachedJwkProvider.java rename to src/main/java/com/auth0/jwk/CaffeineCachedJwkProvider.java index 518e2da..d6b570f 100644 --- a/src/main/java/com/auth0/jwk/GuavaCachedJwkProvider.java +++ b/src/main/java/com/auth0/jwk/CaffeineCachedJwkProvider.java @@ -1,17 +1,18 @@ package com.auth0.jwk; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + import java.time.Duration; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.function.Function; /** - * Jwk provider that caches previously obtained Jwk in memory using a Google Guava cache + * Jwk provider that caches previously obtained Jwk in memory using a Ben Manes' Caffine cache */ @SuppressWarnings("WeakerAccess") -public class GuavaCachedJwkProvider implements JwkProvider { +public class CaffeineCachedJwkProvider implements JwkProvider { private final Cache cache; private final JwkProvider provider; @@ -23,7 +24,7 @@ public class GuavaCachedJwkProvider implements JwkProvider { * * @param provider fallback provider to use when jwk is not cached */ - public GuavaCachedJwkProvider(final JwkProvider provider) { + public CaffeineCachedJwkProvider(final JwkProvider provider) { this(provider, 5, Duration.ofMinutes(10)); } @@ -35,9 +36,9 @@ public GuavaCachedJwkProvider(final JwkProvider provider) { * @param expiresIn amount of time a jwk will live in the cache * @param expiresUnit unit of the expiresIn parameter */ - public GuavaCachedJwkProvider(final JwkProvider provider, long size, long expiresIn, TimeUnit expiresUnit) { + public CaffeineCachedJwkProvider(final JwkProvider provider, long size, long expiresIn, TimeUnit expiresUnit) { this.provider = provider; - this.cache = CacheBuilder.newBuilder() + this.cache = Caffeine.newBuilder() .maximumSize(size) // configure using timeunit; see https://github.com/auth0/jwks-rsa-java/issues/136 .expireAfterWrite(expiresIn, expiresUnit) @@ -47,11 +48,11 @@ public GuavaCachedJwkProvider(final JwkProvider provider, long size, long expire /** * Creates a new cached provider specifying cache size and ttl * - * @param provider fallback provider to use when jwk is not cached - * @param size number of jwt to cache - * @param expiresIn amount of time a jwk will live in the cache + * @param provider fallback provider to use when jwk is not cached + * @param size number of jwt to cache + * @param expiresIn amount of time a jwk will live in the cache */ - public GuavaCachedJwkProvider(final JwkProvider provider, long size, Duration expiresIn) { + public CaffeineCachedJwkProvider(final JwkProvider provider, long size, Duration expiresIn) { this(provider, size, expiresIn.toMillis(), TimeUnit.MILLISECONDS); } @@ -59,8 +60,19 @@ public GuavaCachedJwkProvider(final JwkProvider provider, long size, Duration ex public Jwk get(final String keyId) throws JwkException { try { String cacheKey = keyId == null ? NULL_KID_KEY : keyId; - return cache.get(cacheKey, () -> provider.get(keyId)); - } catch (ExecutionException e) { + return cache.get(cacheKey, new Function() { + @Override + public Jwk apply(String ignored) { + try { + // key passed to apply function is ignored, as we want to + // always use the real keyId here, even if it is null. + return provider.get(keyId); + } catch (JwkException e) { + throw new RuntimeException(e); + } + } + }); + } catch (Throwable e) { // throw the proper exception directly, see https://github.com/auth0/jwks-rsa-java/issues/165 // cause should always be JwkException, but check just to be safe if (e.getCause() instanceof JwkException) { diff --git a/src/main/java/com/auth0/jwk/JwkProviderBuilder.java b/src/main/java/com/auth0/jwk/JwkProviderBuilder.java index e2681a4..39b853f 100644 --- a/src/main/java/com/auth0/jwk/JwkProviderBuilder.java +++ b/src/main/java/com/auth0/jwk/JwkProviderBuilder.java @@ -177,7 +177,7 @@ public JwkProvider build() { urlProvider = new RateLimitedJwkProvider(urlProvider, bucket); } if (this.cached) { - urlProvider = new GuavaCachedJwkProvider(urlProvider, cacheSize, expiresIn); + urlProvider = new CaffeineCachedJwkProvider(urlProvider, cacheSize, expiresIn); } return urlProvider; } diff --git a/src/test/java/com/auth0/jwk/GuavaCachedJwkProviderTest.java b/src/test/java/com/auth0/jwk/CaffeineCachedJwkProviderTest.java similarity index 85% rename from src/test/java/com/auth0/jwk/GuavaCachedJwkProviderTest.java rename to src/test/java/com/auth0/jwk/CaffeineCachedJwkProviderTest.java index 1aca4bd..385bff1 100644 --- a/src/test/java/com/auth0/jwk/GuavaCachedJwkProviderTest.java +++ b/src/test/java/com/auth0/jwk/CaffeineCachedJwkProviderTest.java @@ -15,10 +15,10 @@ import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) -public class GuavaCachedJwkProviderTest { +public class CaffeineCachedJwkProviderTest { private static final String KID = "KID"; - private GuavaCachedJwkProvider provider; + private CaffeineCachedJwkProvider provider; @Mock private JwkProvider fallback; @@ -31,7 +31,7 @@ public class GuavaCachedJwkProviderTest { @Before public void setUp() { - provider = new GuavaCachedJwkProvider(fallback); + provider = new CaffeineCachedJwkProvider(fallback); } @Test @@ -79,12 +79,12 @@ public void shouldUseCachedValue() throws Exception { @Test public void shouldCacheWhenIdMatchesDefaultMissingIdKey() throws Exception { - when(fallback.get(eq(GuavaCachedJwkProvider.NULL_KID_KEY))).thenReturn(jwk); - assertThat(provider.get(GuavaCachedJwkProvider.NULL_KID_KEY), equalTo(jwk)); - verify(fallback).get(eq(GuavaCachedJwkProvider.NULL_KID_KEY)); + when(fallback.get(eq(CaffeineCachedJwkProvider.NULL_KID_KEY))).thenReturn(jwk); + assertThat(provider.get(CaffeineCachedJwkProvider.NULL_KID_KEY), equalTo(jwk)); + verify(fallback).get(eq(CaffeineCachedJwkProvider.NULL_KID_KEY)); verifyNoMoreInteractions(fallback); - assertThat(provider.get(GuavaCachedJwkProvider.NULL_KID_KEY), equalTo(jwk)); + assertThat(provider.get(CaffeineCachedJwkProvider.NULL_KID_KEY), equalTo(jwk)); } @Test diff --git a/src/test/java/com/auth0/jwk/JwkProviderBuilderTest.java b/src/test/java/com/auth0/jwk/JwkProviderBuilderTest.java index 380b2be..3b80e36 100644 --- a/src/test/java/com/auth0/jwk/JwkProviderBuilderTest.java +++ b/src/test/java/com/auth0/jwk/JwkProviderBuilderTest.java @@ -60,8 +60,8 @@ public void shouldCreateCachedProvider() { .cached(true) .build(); assertThat(provider, notNullValue()); - assertThat(provider, instanceOf(GuavaCachedJwkProvider.class)); - assertThat(((GuavaCachedJwkProvider) provider).getBaseProvider(), instanceOf(UrlJwkProvider.class)); + assertThat(provider, instanceOf(CaffeineCachedJwkProvider.class)); + assertThat(((CaffeineCachedJwkProvider) provider).getBaseProvider(), instanceOf(UrlJwkProvider.class)); } @Test @@ -71,8 +71,8 @@ public void shouldCreateCachedProviderWithCustomValues() { .cached(10, 24, TimeUnit.HOURS) .build(); assertThat(provider, notNullValue()); - assertThat(provider, instanceOf(GuavaCachedJwkProvider.class)); - assertThat(((GuavaCachedJwkProvider) provider).getBaseProvider(), instanceOf(UrlJwkProvider.class)); + assertThat(provider, instanceOf(CaffeineCachedJwkProvider.class)); + assertThat(((CaffeineCachedJwkProvider) provider).getBaseProvider(), instanceOf(UrlJwkProvider.class)); } @Test @@ -104,8 +104,8 @@ public void shouldCreateCachedAndRateLimitedProvider() { .rateLimited(true) .build(); assertThat(provider, notNullValue()); - assertThat(provider, instanceOf(GuavaCachedJwkProvider.class)); - JwkProvider baseProvider = ((GuavaCachedJwkProvider) provider).getBaseProvider(); + assertThat(provider, instanceOf(CaffeineCachedJwkProvider.class)); + JwkProvider baseProvider = ((CaffeineCachedJwkProvider) provider).getBaseProvider(); assertThat(baseProvider, instanceOf(RateLimitedJwkProvider.class)); assertThat(((RateLimitedJwkProvider) baseProvider).getBaseProvider(), instanceOf(UrlJwkProvider.class)); } @@ -117,8 +117,8 @@ public void shouldCreateCachedAndRateLimitedProviderWithCustomValues() { .rateLimited(10, 24, TimeUnit.HOURS) .build(); assertThat(provider, notNullValue()); - assertThat(provider, instanceOf(GuavaCachedJwkProvider.class)); - JwkProvider baseProvider = ((GuavaCachedJwkProvider) provider).getBaseProvider(); + assertThat(provider, instanceOf(CaffeineCachedJwkProvider.class)); + JwkProvider baseProvider = ((CaffeineCachedJwkProvider) provider).getBaseProvider(); assertThat(baseProvider, instanceOf(RateLimitedJwkProvider.class)); assertThat(((RateLimitedJwkProvider) baseProvider).getBaseProvider(), instanceOf(UrlJwkProvider.class)); } @@ -127,9 +127,9 @@ public void shouldCreateCachedAndRateLimitedProviderWithCustomValues() { public void shouldCreateCachedAndRateLimitedProviderByDefault() { JwkProvider provider = new JwkProviderBuilder(domain).build(); assertThat(provider, notNullValue()); - assertThat(provider, instanceOf(GuavaCachedJwkProvider.class)); + assertThat(provider, instanceOf(CaffeineCachedJwkProvider.class)); - JwkProvider wrappedCachedProvider = ((GuavaCachedJwkProvider) provider).getBaseProvider(); + JwkProvider wrappedCachedProvider = ((CaffeineCachedJwkProvider) provider).getBaseProvider(); assertThat(wrappedCachedProvider, instanceOf(RateLimitedJwkProvider.class)); JwkProvider wrappedRateLimitedProvider = ((RateLimitedJwkProvider) wrappedCachedProvider).getBaseProvider(); diff --git a/src/test/java/com/auth0/jwk/JwkTest.java b/src/test/java/com/auth0/jwk/JwkTest.java index 5d24f6d..2306a62 100644 --- a/src/test/java/com/auth0/jwk/JwkTest.java +++ b/src/test/java/com/auth0/jwk/JwkTest.java @@ -1,7 +1,6 @@ package com.auth0.jwk; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; + import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -9,9 +8,7 @@ import java.security.SecureRandom; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; -import java.util.Base64; -import java.util.List; -import java.util.Map; +import java.util.*; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; @@ -43,7 +40,7 @@ public class JwkTest { private static final String MODULUS = "vGChUGMTWZNfRsXxd-BtzC4RDYOMqtIhWHol--HNib5SgudWBg6rEcxvR6LWrx57N6vfo68wwT9_FHlZpaK6NXA_dWFW4f3NftfWLL7Bqy90sO4vijM6LMSE6rnl5VB9_Gsynk7_jyTgYWdTwKur0YRec93eha9oCEXmy7Ob1I2dJ8OQmv2GlvA7XZalMxAq4rFnXLzNQ7hCsHrUJP1p7_7SolWm9vTokkmckzSI_mAH2R27Z56DmI7jUkL9fLU-jz-fz4bkNg-mPz4R-kUmM_ld3-xvto79BtxJvOw5qqtLNnRjiDzoqRv-WrBdw5Vj8Pvrg1fwscfVWHlmq-1pFQ"; private static final String EXPONENT = "AQAB"; private static final String CERT_CHAIN = "CERT_CHAIN"; - private static final List KEY_OPS_LIST = Lists.newArrayList("sign"); + private static final List KEY_OPS_LIST = Arrays.asList("sign"); private static final String KEY_OPS_STRING = "sign"; @Rule @@ -164,7 +161,7 @@ public void shouldReturnPublicKeyForNullKeyOpsParam() throws Exception { @Test public void shouldReturnPublicKeyForEmptyKeyOpsParam() throws Exception { final String kid = randomKeyId(); - Map values = publicKeyRsaValues(kid, Lists.newArrayList()); + Map values = publicKeyRsaValues(kid, Arrays.asList()); Jwk jwk = Jwk.fromValues(values); assertThat(jwk.getPublicKey(), notNullValue()); @@ -217,7 +214,7 @@ private static String randomKeyId() { } private static Map unsupportedValues(String kid) { - Map values = Maps.newHashMap(); + Map values = new HashMap(); values.put("alg", "AES_256"); values.put("kty", AES); values.put("use", SIG); @@ -226,12 +223,12 @@ private static Map unsupportedValues(String kid) { } private static Map publicKeyRsaValues(String kid, Object keyOps) { - Map values = Maps.newHashMap(); + Map values = new HashMap(); values.put("alg", RS_256); values.put("kty", RSA); values.put("use", SIG); values.put("key_ops", keyOps); - values.put("x5c", Lists.newArrayList(CERT_CHAIN)); + values.put("x5c", Arrays.asList(CERT_CHAIN)); values.put("x5t", THUMBPRINT); values.put("kid", kid); values.put("n", MODULUS); @@ -240,7 +237,7 @@ private static Map publicKeyRsaValues(String kid, Object keyOps) } private static Map publicKeyEllipticCurveValues(String kid, String alg, Object keyOps, String crv, String x, String y) { - Map values = Maps.newHashMap(); + Map values = new HashMap(); values.put("alg", alg); values.put("kty", EC); values.put("use", SIG);