Custom CacheResolver not working

tommylii picture tommylii · May 26, 2015 · Viewed 8.9k times · Source

I have a Spring Boot project with a custom CacheResolver as I need to decide on runtime which cache I want to use, I don't have any compilation errors but, when I do some tests and place a break point at my custom CacheResolver it never steps into it.

This is my Configuration class for the Cache:

@Configuration
@EnableCaching(proxyTargetClass = true)
@PropertySource(CacheConfig.CLASSPATH_DEPLOY_CACHE_PROPERTIES_PROPERTIES)
public class CacheConfig extends CachingConfigurerSupport{

      public static final String CLASSPATH_DEPLOY_CACHE_PROPERTIES_PROPERTIES = "classpath:/deploy/cache-properties.properties";

      public static final String CACHEABLE_DOCUMENTS_PROPERTY = "cacheable.documents";
      public static final String TTL_CACHEABLE_DOCUMENTS_PROPERTY = "ttl.cacheable.documents";
      public static final String SIZED_CACHEABLE_DOCUMENTS_PROPERTY = "sized.cacheable.documents";
      public static final String CACHE_NAME = "permanentCache";
      public static final String TTL_CACHE = "ttlCache";
      public static final String SIZED_CACHE = "sizedCache";
      public static final String CACHEABLE_DOCUMENTS = "cacheableDocuments";
      public static final String SIZED_CACHEABLE_DOCUMENTS = "sizedCacheableDocuments";
      public static final int WEIGHT = 1000000;
      public static final int TO_KBYTES = 1000;

      @Inject
      protected Environment environment;

      //@Bean
      @Override
      public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        GuavaCache sizedCache = new GuavaCache(SIZED_CACHE, CacheBuilder.newBuilder().maximumWeight(WEIGHT).weigher(
                (key, storable) -> {
                  String json = ((Storable) storable).toJson();
                  return json.getBytes().length / TO_KBYTES;
                }
        ).build());
        GuavaCache permanentCache = new GuavaCache(CACHE_NAME,CacheBuilder.newBuilder().build());
        //GuavaCache ttlCache = new GuavaCache(TTL_CACHE, CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).build());
        cacheManager.setCaches(Arrays.asList(permanentCache,sizedCache));
        return cacheManager;
      }

      @Bean(name = "wgstCacheResolver")
      @Override
      public CacheResolver cacheResolver(){
        CacheResolver cacheResolver = new WgstCacheResolver(cacheManager(),cacheableDocuments(),sizedCacheableDocuments());
        return cacheResolver;
      }


      @Bean(name = CACHEABLE_DOCUMENTS)
      public List<String> cacheableDocuments(){
        String[] cacheableDocuments = StringUtils.commaDelimitedListToStringArray(environment.getProperty(CACHEABLE_DOCUMENTS_PROPERTY));
        return Arrays.asList(cacheableDocuments);
      }

      @Bean(name = SIZED_CACHEABLE_DOCUMENTS)
      public List<String> sizedCacheableDocuments(){
        String[] sizedCacheableDocuments = StringUtils.commaDelimitedListToStringArray(environment.getProperty(SIZED_CACHEABLE_DOCUMENTS_PROPERTY));
        return Arrays.asList(sizedCacheableDocuments);
      } 
    }

Here is my CacheResolver

public class WgstCacheResolver extends AbstractCacheResolver {

  private final List<String> cacheableDocuments;
  private final List<String> sizedCacheableDocuments;

  public WgstCacheResolver(final CacheManager cacheManager,final List<String> cacheableDocuments, final List<String> sizedCacheableDocuments) {
    super(cacheManager);
    this.cacheableDocuments = cacheableDocuments;
    this.sizedCacheableDocuments = sizedCacheableDocuments;
  }

  /**
   * Resolves the cache(s) to be updated on runtime
   * @param context
   * @return*/
  @Override
  protected Collection<String> getCacheNames(final CacheOperationInvocationContext<?> context) {

    final Collection<String> cacheNames = new ArrayList<>();
    final AbstractDao dao = (AbstractDao)context.getTarget();
    final String documentType = dao.getDocumentType().toString();
    if (cacheableDocuments.contains(documentType)){
      cacheNames.add("permanentCache");
    }
    if (sizedCacheableDocuments.contains(documentType)){
      cacheNames.add("sizedCache");
    }
    return cacheNames;
  }
}

And here my DAO where I use the cache:

    @Component
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.DEFAULT)
    @CacheConfig(cacheResolver = "wgstCacheResolver")
    public class CacheableDao<T extends Storable> extends AbstractDao<T> {

      private final Logger logger = LoggerFactory.getLogger(CacheableDao.class);

      public CacheableDao(final Bucket bucket, final Class<T> typeParameterClass, final DocumentType documentType) {
        super(bucket, typeParameterClass, documentType);
      }

      @Cacheable(key = "{#root.methodName, #root.target.generateFullKey(#key)}")
      public T get(final String key) throws DatastoreAccessException, ObjectMappingException {
        //do something
      }
.
.
.
}

I have tried implementing CacheResolver instead of extending AbstractCacheResolver but it didn't make any difference.

Thank you.

Answer

tommylii picture tommylii · May 27, 2015

Cache names need to be included at some point, just specifying the CacheResolver to use is not enough, the @Cacheable class needs to be aware of the available cache names, so I included them with the @CacheConfig annotation:

@CacheConfig(cacheNames = {WgstCacheConfig.PERMANENT_CACHE, WgstCacheConfig.SIZED_CACHE},
    cacheResolver = WgstCacheConfig.WGST_CACHE_RESOLVER)
public class CacheableDao<T extends Storable> extends AbstractDao<T> {

One thing that I don't like is that I need to provide a null CacheManager, even if I'm not using it, otherwise I get the following error:

Caused by: java.lang.IllegalStateException: No CacheResolver specified, and no bean of type CacheManager found. Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.

So I left it like this, and it works:

  @Bean
  public CacheManager cacheManager() {
    return null;
  }

  @Bean(name = WGST_CACHE_RESOLVER)
  public CacheResolver cacheResolver(){
    CacheResolver cacheResolver = new WgstCacheResolver(cacheableDocuments(),sizedCacheableDocuments(),getPermanentCache(),
                                                        getSizedCache());
    return cacheResolver;
  }

Reran my tests, stepping through my custom CacheResolver and it is behaving as expected resolving to the correct cache(s)

My configuration class is not extending CachingConfigurerSupport anymore.