D.B
D.B

Reputation: 4309

Set cache expireAfterWrite property dynamically - Caffeine and Spring WebFlux

I am using caffeine cache to store an authorisation token that has been obtained using webClient WebFlux. I have set the expireAfterWrite to a hardcoded value in the application.yml file as follows:

spring:
  cache:
    cache-names: accessTokens
    caffeine:
      spec: expireAfterWrite=100m

The token is obtained using a WebClient with Spring WebFlux as below code depicts:

 @Autowired
 var cacheManager: CacheManager? = null

 override fun getAuthToken(): Mono<AccessToken> {

    val map = LinkedMultiValueMap<String, String>()
    map.add("client_id", clientId)
    map.add("client_secret", clientSecret)
    map.add("grant_type", "client_credentials")

    var cachedVersion = this.cacheManager?.getCache("accessTokens");
    if (cachedVersion?.get("tokens") != null) {
        val token = cachedVersion.get("tokens")
        return Mono.just(token?.get() as AccessToken)
    } else {

        return webClient.post()
                .uri("/client-credentials/token")
                .body(BodyInserters.fromFormData(map))
                .retrieve()
                .onStatus(HttpStatus::is5xxServerError) {
                    ClientLogger.logClientErrorResponse(it, errorResponse)
                }
                .onStatus(HttpStatus::is4xxClientError) {
                    ClientLogger.logClientErrorResponse(it, errorResponse)
                }
                .bodyToMono(AccessToken::class.java)
                .doOnNext { response ->       
                      // Set here the expiration time of the cache based on  
                      // response.expiresIn              
                      this.cacheManager?.getCache("accessTokens")?.put("tokens", response) }
                .log()
    }

}

I am storing the token after the data is emitted/returned successfully within the .doOnNext() method but i need to be able to set the expiration time or refresh the hardcoded expiration time of the cache based on the expiresIn property that is part of the response object,

      .doOnNext { response ->    
                      // Set here the expiration time of the cache based on  
                      // response.expiresIn           
                      this.cacheManager?.getCache("accessTokens")?.put("tokens", response) 
                 }

Any ideas would be much appreciated.

Upvotes: 0

Views: 3246

Answers (1)

Ben Manes
Ben Manes

Reputation: 9621

// Policy to set the lifetime based on when the entry was created
var expiresAfterCreate = new Expiry<String, AccessToken>() {
  public long expireAfterCreate(String credentials, AccessToken token, long currentTime) {
    Duration duration = token.expiresIn();
    return token.toNanos();
  }
  public long expireAfterUpdate(String credentials, AccessToken token, 
      long currentTime, long currentDuration) {
    return currentDuration;
  }
  public long expireAfterRead(String credentials, AccessToken token,
      long currentTime, long currentDuration) {
    return currentDuration;
  }
});

// CompletableFuture-based cache
AsyncLoadingCache<String, AccessToken> cache = Caffeine.newBuilder()
    .expireAfter(expiresAfterCreate)
    .buildAsync((credentials, executor) -> {
      Mono<AccessToken> token = retrieve(credentials);
      return token.toFuture();
    });

// Get from cache, loading if absent, and converts to Mono
Mono<AccessToken> getAuthToken() {
  var token = cache.get(credentials);
  return Mono.fromFuture(token);
}

Upvotes: 2

Related Questions