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

Replace guava with caffeine. #177

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, Jwk> cache;
private final JwkProvider provider;
Expand All @@ -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));
}

Expand All @@ -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)
Expand All @@ -47,20 +48,31 @@ 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);
}

@Override
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<String, Jwk>() {
@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) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/auth0/jwk/JwkProviderBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,7 +31,7 @@ public class GuavaCachedJwkProviderTest {

@Before
public void setUp() {
provider = new GuavaCachedJwkProvider(fallback);
provider = new CaffeineCachedJwkProvider(fallback);
}

@Test
Expand Down Expand Up @@ -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
Expand Down
20 changes: 10 additions & 10 deletions src/test/java/com/auth0/jwk/JwkProviderBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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));
}
Expand All @@ -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));
}
Expand All @@ -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();
Expand Down
19 changes: 8 additions & 11 deletions src/test/java/com/auth0/jwk/JwkTest.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
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;

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;
Expand Down Expand Up @@ -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<String> KEY_OPS_LIST = Lists.newArrayList("sign");
private static final List<String> KEY_OPS_LIST = Arrays.asList("sign");
private static final String KEY_OPS_STRING = "sign";

@Rule
Expand Down Expand Up @@ -164,7 +161,7 @@ public void shouldReturnPublicKeyForNullKeyOpsParam() throws Exception {
@Test
public void shouldReturnPublicKeyForEmptyKeyOpsParam() throws Exception {
final String kid = randomKeyId();
Map<String, Object> values = publicKeyRsaValues(kid, Lists.newArrayList());
Map<String, Object> values = publicKeyRsaValues(kid, Arrays.asList());
Jwk jwk = Jwk.fromValues(values);

assertThat(jwk.getPublicKey(), notNullValue());
Expand Down Expand Up @@ -217,7 +214,7 @@ private static String randomKeyId() {
}

private static Map<String, Object> unsupportedValues(String kid) {
Map<String, Object> values = Maps.newHashMap();
Map<String, Object> values = new HashMap();
values.put("alg", "AES_256");
values.put("kty", AES);
values.put("use", SIG);
Expand All @@ -226,12 +223,12 @@ private static Map<String, Object> unsupportedValues(String kid) {
}

private static Map<String, Object> publicKeyRsaValues(String kid, Object keyOps) {
Map<String, Object> values = Maps.newHashMap();
Map<String, Object> 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);
Expand All @@ -240,7 +237,7 @@ private static Map<String, Object> publicKeyRsaValues(String kid, Object keyOps)
}

private static Map<String, Object> publicKeyEllipticCurveValues(String kid, String alg, Object keyOps, String crv, String x, String y) {
Map<String, Object> values = Maps.newHashMap();
Map<String, Object> values = new HashMap();
values.put("alg", alg);
values.put("kty", EC);
values.put("use", SIG);
Expand Down