1 /*
2 * Copyright 2010-2026 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.mybatis.caches.ehcache;
17
18 import java.util.concurrent.locks.ReadWriteLock;
19
20 import net.sf.ehcache.CacheManager;
21 import net.sf.ehcache.Ehcache;
22 import net.sf.ehcache.Element;
23
24 import org.apache.ibatis.cache.Cache;
25
26 /**
27 * Cache adapter for Ehcache.
28 *
29 * @author Simone Tripodi
30 */
31 public abstract class AbstractEhcacheCache implements Cache {
32
33 /**
34 * The cache manager reference.
35 */
36 protected static CacheManager CACHE_MANAGER = CacheManager.create();
37
38 /**
39 * The cache id (namespace).
40 */
41 protected final String id;
42
43 /**
44 * The cache instance.
45 */
46 protected Ehcache cache;
47
48 /**
49 * Instantiates a new abstract ehcache cache.
50 *
51 * @param id
52 * the cache id (namespace)
53 */
54 public AbstractEhcacheCache(final String id) {
55 if (id == null) {
56 throw new IllegalArgumentException("Cache instances require an ID");
57 }
58 this.id = id;
59 }
60
61 /**
62 * {@inheritDoc}
63 */
64 @Override
65 public void clear() {
66 cache.removeAll();
67 }
68
69 /**
70 * {@inheritDoc}
71 */
72 @Override
73 public String getId() {
74 return id;
75 }
76
77 /**
78 * {@inheritDoc}
79 */
80 @Override
81 public Object getObject(Object key) {
82 Element cachedElement = cache.get(key);
83 if (cachedElement == null) {
84 return null;
85 }
86 return cachedElement.getObjectValue();
87 }
88
89 /**
90 * {@inheritDoc}
91 */
92 @Override
93 public int getSize() {
94 return cache.getSize();
95 }
96
97 /**
98 * {@inheritDoc}
99 */
100 @Override
101 public void putObject(Object key, Object value) {
102 cache.put(new Element(key, value));
103 }
104
105 /**
106 * {@inheritDoc}
107 */
108 @Override
109 public Object removeObject(Object key) {
110 Object obj = getObject(key);
111 cache.remove(key);
112 return obj;
113 }
114
115 /**
116 * {@inheritDoc}
117 */
118 public void unlock(Object key) {
119 }
120
121 /**
122 * {@inheritDoc}
123 */
124 @Override
125 public boolean equals(Object obj) {
126 if (this == obj) {
127 return true;
128 }
129 if (obj == null) {
130 return false;
131 }
132 if (!(obj instanceof Cache)) {
133 return false;
134 }
135
136 Cache otherCache = (Cache) obj;
137 return id.equals(otherCache.getId());
138 }
139
140 /**
141 * {@inheritDoc}
142 */
143 @Override
144 public int hashCode() {
145 return id.hashCode();
146 }
147
148 @Override
149 public ReadWriteLock getReadWriteLock() {
150 return null;
151 }
152
153 /**
154 * {@inheritDoc}
155 */
156 @Override
157 public String toString() {
158 return "EHCache {" + id + "}";
159 }
160
161 // DYNAMIC PROPERTIES
162
163 /**
164 * Sets the time to idle for an element before it expires. Is only used if the element is not eternal.
165 *
166 * @param timeToIdleSeconds
167 * the default amount of time to live for an element from its last accessed or modified date
168 */
169 public void setTimeToIdleSeconds(long timeToIdleSeconds) {
170 cache.getCacheConfiguration().setTimeToIdleSeconds(timeToIdleSeconds);
171 }
172
173 /**
174 * Sets the time to idle for an element before it expires. Is only used if the element is not eternal.
175 *
176 * @param timeToLiveSeconds
177 * the default amount of time to live for an element from its creation date
178 */
179 public void setTimeToLiveSeconds(long timeToLiveSeconds) {
180 cache.getCacheConfiguration().setTimeToLiveSeconds(timeToLiveSeconds);
181 }
182
183 /**
184 * Sets the maximum objects to be held in memory (0 = no limit).
185 *
186 * @param maxEntriesLocalHeap
187 * The maximum number of elements in heap, before they are evicted (0 == no limit)
188 */
189 public void setMaxEntriesLocalHeap(long maxEntriesLocalHeap) {
190 cache.getCacheConfiguration().setMaxEntriesLocalHeap(maxEntriesLocalHeap);
191 }
192
193 /**
194 * Sets the maximum number elements on Disk. 0 means unlimited.
195 *
196 * @param maxEntriesLocalDisk
197 * the maximum number of Elements to allow on the disk. 0 means unlimited.
198 */
199 public void setMaxEntriesLocalDisk(long maxEntriesLocalDisk) {
200 cache.getCacheConfiguration().setMaxEntriesLocalDisk(maxEntriesLocalDisk);
201 }
202
203 /**
204 * Sets the maximum bytes to be used for the disk tier. When greater than zero the cache will overflow to disk when
205 * the heap tier is full.
206 * <p>
207 * In Ehcache 2, {@code maxBytesLocalDisk} and {@code maxEntriesLocalDisk} are mutually exclusive, and the disk pool
208 * type cannot be changed on a running cache instance. This method therefore removes and recreates the underlying
209 * cache with a fresh {@link net.sf.ehcache.config.CacheConfiguration} that applies the requested byte limit while
210 * preserving the other settings (TTI, TTL, heap size, eviction policy) from the current configuration.
211 * </p>
212 *
213 * @param maxBytesLocalDisk
214 * the maximum number of bytes to allocate on disk. 0 means no disk tier (heap-only).
215 */
216 public void setMaxBytesLocalDisk(long maxBytesLocalDisk) {
217 net.sf.ehcache.config.CacheConfiguration current = cache.getCacheConfiguration();
218 // Build a fresh CacheConfiguration so that onDiskPoolUsage is unset and setMaxBytesLocalDisk
219 // can be applied without triggering the "can't switch disk pool" guard in Ehcache 2.
220 net.sf.ehcache.config.CacheConfiguration newConfig = new net.sf.ehcache.config.CacheConfiguration(id,
221 (int) current.getMaxEntriesLocalHeap());
222 newConfig.setTimeToIdleSeconds(current.getTimeToIdleSeconds());
223 newConfig.setTimeToLiveSeconds(current.getTimeToLiveSeconds());
224 newConfig.setEternal(current.isEternal());
225 newConfig.setMemoryStoreEvictionPolicy(current.getMemoryStoreEvictionPolicy().toString());
226 newConfig.setMaxBytesLocalDisk(maxBytesLocalDisk);
227 rebuildCacheWith(newConfig);
228 }
229
230 /**
231 * Removes the existing cache from the {@link CacheManager} and registers a new one built from {@code newConfig}.
232 * Subclasses may override this method to apply additional decorators (e.g.
233 * {@link net.sf.ehcache.constructs.blocking.BlockingCache}).
234 *
235 * @param newConfig
236 * the configuration to use for the replacement cache
237 */
238 protected void rebuildCacheWith(net.sf.ehcache.config.CacheConfiguration newConfig) {
239 CACHE_MANAGER.removeCache(id);
240 CACHE_MANAGER.addCache(new net.sf.ehcache.Cache(newConfig));
241 this.cache = CACHE_MANAGER.getEhcache(id);
242 }
243
244 /**
245 * Sets the eviction policy. An invalid argument will set it to null.
246 *
247 * @param memoryStoreEvictionPolicy
248 * a String representation of the policy. One of "LRU", "LFU" or "FIFO".
249 */
250 public void setMemoryStoreEvictionPolicy(String memoryStoreEvictionPolicy) {
251 cache.getCacheConfiguration().setMemoryStoreEvictionPolicy(memoryStoreEvictionPolicy);
252 }
253
254 }