001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.cache.caffeine; 018 019import java.util.concurrent.Callable; 020import java.util.function.Function; 021 022import com.github.benmanes.caffeine.cache.LoadingCache; 023 024import org.springframework.cache.support.AbstractValueAdaptingCache; 025import org.springframework.lang.UsesJava8; 026import org.springframework.util.Assert; 027 028/** 029 * Spring {@link org.springframework.cache.Cache} adapter implementation 030 * on top of a Caffeine {@link com.github.benmanes.caffeine.cache.Cache} instance. 031 * 032 * <p>Requires Caffeine 2.1 or higher. 033 * 034 * @author Ben Manes 035 * @author Juergen Hoeller 036 * @author Stephane Nicoll 037 * @since 4.3 038 * @see CaffeineCacheManager 039 */ 040@UsesJava8 041public class CaffeineCache extends AbstractValueAdaptingCache { 042 043 private final String name; 044 045 private final com.github.benmanes.caffeine.cache.Cache<Object, Object> cache; 046 047 048 /** 049 * Create a {@link CaffeineCache} instance with the specified name and the 050 * given internal {@link com.github.benmanes.caffeine.cache.Cache} to use. 051 * @param name the name of the cache 052 * @param cache the backing Caffeine Cache instance 053 */ 054 public CaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache<Object, Object> cache) { 055 this(name, cache, true); 056 } 057 058 /** 059 * Create a {@link CaffeineCache} instance with the specified name and the 060 * given internal {@link com.github.benmanes.caffeine.cache.Cache} to use. 061 * @param name the name of the cache 062 * @param cache the backing Caffeine Cache instance 063 * @param allowNullValues whether to accept and convert {@code null} 064 * values for this cache 065 */ 066 public CaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache<Object, Object> cache, 067 boolean allowNullValues) { 068 069 super(allowNullValues); 070 Assert.notNull(name, "Name must not be null"); 071 Assert.notNull(cache, "Cache must not be null"); 072 this.name = name; 073 this.cache = cache; 074 } 075 076 077 @Override 078 public final String getName() { 079 return this.name; 080 } 081 082 @Override 083 public final com.github.benmanes.caffeine.cache.Cache<Object, Object> getNativeCache() { 084 return this.cache; 085 } 086 087 @Override 088 public ValueWrapper get(Object key) { 089 if (this.cache instanceof LoadingCache) { 090 Object value = ((LoadingCache<Object, Object>) this.cache).get(key); 091 return toValueWrapper(value); 092 } 093 return super.get(key); 094 } 095 096 @SuppressWarnings("unchecked") 097 @Override 098 public <T> T get(Object key, final Callable<T> valueLoader) { 099 return (T) fromStoreValue(this.cache.get(key, new LoadFunction(valueLoader))); 100 } 101 102 @Override 103 protected Object lookup(Object key) { 104 return this.cache.getIfPresent(key); 105 } 106 107 @Override 108 public void put(Object key, Object value) { 109 this.cache.put(key, toStoreValue(value)); 110 } 111 112 @Override 113 public ValueWrapper putIfAbsent(Object key, final Object value) { 114 PutIfAbsentFunction callable = new PutIfAbsentFunction(value); 115 Object result = this.cache.get(key, callable); 116 return (callable.called ? null : toValueWrapper(result)); 117 } 118 119 @Override 120 public void evict(Object key) { 121 this.cache.invalidate(key); 122 } 123 124 @Override 125 public void clear() { 126 this.cache.invalidateAll(); 127 } 128 129 130 private class PutIfAbsentFunction implements Function<Object, Object> { 131 132 private final Object value; 133 134 private boolean called; 135 136 public PutIfAbsentFunction(Object value) { 137 this.value = value; 138 } 139 140 @Override 141 public Object apply(Object key) { 142 this.called = true; 143 return toStoreValue(this.value); 144 } 145 } 146 147 148 private class LoadFunction implements Function<Object, Object> { 149 150 private final Callable<?> valueLoader; 151 152 public LoadFunction(Callable<?> valueLoader) { 153 this.valueLoader = valueLoader; 154 } 155 156 @Override 157 public Object apply(Object o) { 158 try { 159 return toStoreValue(valueLoader.call()); 160 } 161 catch (Exception ex) { 162 throw new ValueRetrievalException(o, valueLoader, ex); 163 } 164 } 165 } 166 167}