Sunday, December 30, 2018

Memoize a Supplier in Java

I have been doing a lot of integration work of late, interacting with third party APIs. Several of these APIs require a token to be present during certain invocations. These token have a given lifespan but are rate limited. That is to say they may be valid for fifteen minutes, while one might be only be able to create one every five minutes. If it is necessary to invoke the APIs more than once per this five minute interval, then one cannot create a new token prior to every invocation. 

Additional, since the issuance of a new token is in some sense "expensive" and given the timing mismatch, it makes sense to "cache" a valid token for the duration of its lifespan. In order to do this, I memoized the Supplier of this token, that is I wrapped the API call to create the new token in a Supplier and then I memoized this Supplier within a memoizing Supplier.

As a bit of background, the Supplier (functional) interface was introduced in Java version 8. It has a single method ("get") which takes no arguments, but returns an Object of type T, in this case, of type String.

Javadoc reference link: https://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html

To memoize this token Supplier, I could create a class that took the delegate Supplier has a constructor argument. Internally, there would be a value object of type T that would contain the result of the invocation of the delegate Supplier. 

public class MemoizingSupplier<T> implements Supplier<T> { 
    final Supplier<T> delegate; 
    private T value; 

    public MemoizingSupplier(final Supplier<T> delegate) { 
        this.delegate = delegate;
    } 
  
    @Override public T get() { 
         if (value == null) { 
             T t = delegate.get(); 
             value = t; 
             return t; 
         } 
         return value;
}}

A better example (and more thread safe version) is found here: https://google.github.io/guava/releases/19.0/api/docs/com/google/common/base/Suppliers.html#memoize(com.google.common.base.Supplier)

As you can see, Guava provides some very convenient memoization utilities that could be used instead of writing your own. In the use case provided above, I wound up using the method memoizeWithExpiration. This memoizing Supplier would expire every ten minutes, ensuring that API invocations would always be able to create a token should they need to.

https://google.github.io/guava/releases/19.0/api/docs/src-html/com/google/common/base/Suppliers.html#line.167