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.ehcache; 018 019import java.util.concurrent.Callable; 020 021import net.sf.ehcache.Ehcache; 022import net.sf.ehcache.Element; 023import net.sf.ehcache.Status; 024 025import org.springframework.cache.Cache; 026import org.springframework.cache.support.SimpleValueWrapper; 027import org.springframework.lang.Nullable; 028import org.springframework.util.Assert; 029 030/** 031 * {@link Cache} implementation on top of an {@link Ehcache} instance. 032 * 033 * @author Costin Leau 034 * @author Juergen Hoeller 035 * @author Stephane Nicoll 036 * @since 3.1 037 * @see EhCacheCacheManager 038 */ 039public class EhCacheCache implements Cache { 040 041 private final Ehcache cache; 042 043 044 /** 045 * Create an {@link EhCacheCache} instance. 046 * @param ehcache the backing Ehcache instance 047 */ 048 public EhCacheCache(Ehcache ehcache) { 049 Assert.notNull(ehcache, "Ehcache must not be null"); 050 Status status = ehcache.getStatus(); 051 if (!Status.STATUS_ALIVE.equals(status)) { 052 throw new IllegalArgumentException( 053 "An 'alive' Ehcache is required - current cache is " + status.toString()); 054 } 055 this.cache = ehcache; 056 } 057 058 059 @Override 060 public final String getName() { 061 return this.cache.getName(); 062 } 063 064 @Override 065 public final Ehcache getNativeCache() { 066 return this.cache; 067 } 068 069 @Override 070 @Nullable 071 public ValueWrapper get(Object key) { 072 Element element = lookup(key); 073 return toValueWrapper(element); 074 } 075 076 @SuppressWarnings("unchecked") 077 @Override 078 @Nullable 079 public <T> T get(Object key, @Nullable Class<T> type) { 080 Element element = this.cache.get(key); 081 Object value = (element != null ? element.getObjectValue() : null); 082 if (value != null && type != null && !type.isInstance(value)) { 083 throw new IllegalStateException( 084 "Cached value is not of required type [" + type.getName() + "]: " + value); 085 } 086 return (T) value; 087 } 088 089 @SuppressWarnings("unchecked") 090 @Override 091 @Nullable 092 public <T> T get(Object key, Callable<T> valueLoader) { 093 Element element = lookup(key); 094 if (element != null) { 095 return (T) element.getObjectValue(); 096 } 097 else { 098 this.cache.acquireWriteLockOnKey(key); 099 try { 100 element = lookup(key); // one more attempt with the write lock 101 if (element != null) { 102 return (T) element.getObjectValue(); 103 } 104 else { 105 return loadValue(key, valueLoader); 106 } 107 } 108 finally { 109 this.cache.releaseWriteLockOnKey(key); 110 } 111 } 112 } 113 114 private <T> T loadValue(Object key, Callable<T> valueLoader) { 115 T value; 116 try { 117 value = valueLoader.call(); 118 } 119 catch (Throwable ex) { 120 throw new ValueRetrievalException(key, valueLoader, ex); 121 } 122 put(key, value); 123 return value; 124 } 125 126 @Override 127 public void put(Object key, @Nullable Object value) { 128 this.cache.put(new Element(key, value)); 129 } 130 131 @Override 132 @Nullable 133 public ValueWrapper putIfAbsent(Object key, @Nullable Object value) { 134 Element existingElement = this.cache.putIfAbsent(new Element(key, value)); 135 return toValueWrapper(existingElement); 136 } 137 138 @Override 139 public void evict(Object key) { 140 this.cache.remove(key); 141 } 142 143 @Override 144 public boolean evictIfPresent(Object key) { 145 return this.cache.remove(key); 146 } 147 148 @Override 149 public void clear() { 150 this.cache.removeAll(); 151 } 152 153 @Override 154 public boolean invalidate() { 155 boolean notEmpty = (this.cache.getSize() > 0); 156 this.cache.removeAll(); 157 return notEmpty; 158 } 159 160 161 @Nullable 162 private Element lookup(Object key) { 163 return this.cache.get(key); 164 } 165 166 @Nullable 167 private ValueWrapper toValueWrapper(@Nullable Element element) { 168 return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null); 169 } 170 171}