001/* 002 * Copyright 2002-2015 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.guava; 018 019import java.util.concurrent.Callable; 020import java.util.concurrent.ExecutionException; 021 022import com.google.common.cache.LoadingCache; 023import com.google.common.util.concurrent.UncheckedExecutionException; 024 025import org.springframework.cache.support.AbstractValueAdaptingCache; 026import org.springframework.util.Assert; 027 028/** 029 * Spring {@link org.springframework.cache.Cache} adapter implementation 030 * on top of a Guava {@link com.google.common.cache.Cache} instance. 031 * 032 * <p>Requires Google Guava 12.0 or higher. 033 * 034 * @author Juergen Hoeller 035 * @author Stephane Nicoll 036 * @since 4.0 037 */ 038public class GuavaCache extends AbstractValueAdaptingCache { 039 040 private final String name; 041 042 private final com.google.common.cache.Cache<Object, Object> cache; 043 044 045 /** 046 * Create a {@link GuavaCache} instance with the specified name and the 047 * given internal {@link com.google.common.cache.Cache} to use. 048 * @param name the name of the cache 049 * @param cache the backing Guava Cache instance 050 */ 051 public GuavaCache(String name, com.google.common.cache.Cache<Object, Object> cache) { 052 this(name, cache, true); 053 } 054 055 /** 056 * Create a {@link GuavaCache} instance with the specified name and the 057 * given internal {@link com.google.common.cache.Cache} to use. 058 * @param name the name of the cache 059 * @param cache the backing Guava Cache instance 060 * @param allowNullValues whether to accept and convert {@code null} 061 * values for this cache 062 */ 063 public GuavaCache(String name, com.google.common.cache.Cache<Object, Object> cache, boolean allowNullValues) { 064 super(allowNullValues); 065 Assert.notNull(name, "Name must not be null"); 066 Assert.notNull(cache, "Cache must not be null"); 067 this.name = name; 068 this.cache = cache; 069 } 070 071 072 @Override 073 public final String getName() { 074 return this.name; 075 } 076 077 @Override 078 public final com.google.common.cache.Cache<Object, Object> getNativeCache() { 079 return this.cache; 080 } 081 082 @Override 083 public ValueWrapper get(Object key) { 084 if (this.cache instanceof LoadingCache) { 085 try { 086 Object value = ((LoadingCache<Object, Object>) this.cache).get(key); 087 return toValueWrapper(value); 088 } 089 catch (ExecutionException ex) { 090 throw new UncheckedExecutionException(ex.getMessage(), ex); 091 } 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 try { 100 return (T) fromStoreValue(this.cache.get(key, new Callable<Object>() { 101 @Override 102 public Object call() throws Exception { 103 return toStoreValue(valueLoader.call()); 104 } 105 })); 106 } 107 catch (ExecutionException ex) { 108 throw new ValueRetrievalException(key, valueLoader, ex.getCause()); 109 } 110 catch (UncheckedExecutionException ex) { 111 throw new ValueRetrievalException(key, valueLoader, ex.getCause()); 112 } 113 } 114 115 @Override 116 protected Object lookup(Object key) { 117 return this.cache.getIfPresent(key); 118 } 119 120 @Override 121 public void put(Object key, Object value) { 122 this.cache.put(key, toStoreValue(value)); 123 } 124 125 @Override 126 public ValueWrapper putIfAbsent(Object key, final Object value) { 127 try { 128 PutIfAbsentCallable callable = new PutIfAbsentCallable(value); 129 Object result = this.cache.get(key, callable); 130 return (callable.called ? null : toValueWrapper(result)); 131 } 132 catch (ExecutionException ex) { 133 throw new IllegalStateException(ex); 134 } 135 } 136 137 @Override 138 public void evict(Object key) { 139 this.cache.invalidate(key); 140 } 141 142 @Override 143 public void clear() { 144 this.cache.invalidateAll(); 145 } 146 147 148 private class PutIfAbsentCallable implements Callable<Object> { 149 150 private final Object value; 151 152 private boolean called; 153 154 public PutIfAbsentCallable(Object value) { 155 this.value = value; 156 } 157 158 @Override 159 public Object call() throws Exception { 160 this.called = true; 161 return toStoreValue(this.value); 162 } 163 } 164 165}