View Javadoc
1   /*
2    *    Copyright 2009-2023 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.apache.ibatis.mapping;
17  
18  import java.lang.reflect.Constructor;
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.Properties;
23  
24  import org.apache.ibatis.builder.InitializingObject;
25  import org.apache.ibatis.cache.Cache;
26  import org.apache.ibatis.cache.CacheException;
27  import org.apache.ibatis.cache.decorators.BlockingCache;
28  import org.apache.ibatis.cache.decorators.LoggingCache;
29  import org.apache.ibatis.cache.decorators.LruCache;
30  import org.apache.ibatis.cache.decorators.ScheduledCache;
31  import org.apache.ibatis.cache.decorators.SerializedCache;
32  import org.apache.ibatis.cache.decorators.SynchronizedCache;
33  import org.apache.ibatis.cache.impl.PerpetualCache;
34  import org.apache.ibatis.reflection.MetaObject;
35  import org.apache.ibatis.reflection.SystemMetaObject;
36  
37  /**
38   * @author Clinton Begin
39   */
40  public class CacheBuilder {
41    private final String id;
42    private Class<? extends Cache> implementation;
43    private final List<Class<? extends Cache>> decorators;
44    private Integer size;
45    private Long clearInterval;
46    private boolean readWrite;
47    private Properties properties;
48    private boolean blocking;
49  
50    public CacheBuilder(String id) {
51      this.id = id;
52      this.decorators = new ArrayList<>();
53    }
54  
55    public CacheBuilder implementation(Class<? extends Cache> implementation) {
56      this.implementation = implementation;
57      return this;
58    }
59  
60    public CacheBuilder addDecorator(Class<? extends Cache> decorator) {
61      if (decorator != null) {
62        this.decorators.add(decorator);
63      }
64      return this;
65    }
66  
67    public CacheBuilder size(Integer size) {
68      this.size = size;
69      return this;
70    }
71  
72    public CacheBuilder clearInterval(Long clearInterval) {
73      this.clearInterval = clearInterval;
74      return this;
75    }
76  
77    public CacheBuilder readWrite(boolean readWrite) {
78      this.readWrite = readWrite;
79      return this;
80    }
81  
82    public CacheBuilder blocking(boolean blocking) {
83      this.blocking = blocking;
84      return this;
85    }
86  
87    public CacheBuilder properties(Properties properties) {
88      this.properties = properties;
89      return this;
90    }
91  
92    public Cache build() {
93      setDefaultImplementations();
94      Cache cache = newBaseCacheInstance(implementation, id);
95      setCacheProperties(cache);
96      // issue #352, do not apply decorators to custom caches
97      if (PerpetualCache.class.equals(cache.getClass())) {
98        for (Class<? extends Cache> decorator : decorators) {
99          cache = newCacheDecoratorInstance(decorator, cache);
100         setCacheProperties(cache);
101       }
102       cache = setStandardDecorators(cache);
103     } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
104       cache = new LoggingCache(cache);
105     }
106     return cache;
107   }
108 
109   private void setDefaultImplementations() {
110     if (implementation == null) {
111       implementation = PerpetualCache.class;
112       if (decorators.isEmpty()) {
113         decorators.add(LruCache.class);
114       }
115     }
116   }
117 
118   private Cache setStandardDecorators(Cache cache) {
119     try {
120       MetaObject metaCache = SystemMetaObject.forObject(cache);
121       if (size != null && metaCache.hasSetter("size")) {
122         metaCache.setValue("size", size);
123       }
124       if (clearInterval != null) {
125         cache = new ScheduledCache(cache);
126         ((ScheduledCache) cache).setClearInterval(clearInterval);
127       }
128       if (readWrite) {
129         cache = new SerializedCache(cache);
130       }
131       cache = new LoggingCache(cache);
132       cache = new SynchronizedCache(cache);
133       if (blocking) {
134         cache = new BlockingCache(cache);
135       }
136       return cache;
137     } catch (Exception e) {
138       throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
139     }
140   }
141 
142   private void setCacheProperties(Cache cache) {
143     if (properties != null) {
144       MetaObject metaCache = SystemMetaObject.forObject(cache);
145       for (Map.Entry<Object, Object> entry : properties.entrySet()) {
146         String name = (String) entry.getKey();
147         String value = (String) entry.getValue();
148         if (metaCache.hasSetter(name)) {
149           Class<?> type = metaCache.getSetterType(name);
150           if (String.class == type) {
151             metaCache.setValue(name, value);
152           } else if (int.class == type || Integer.class == type) {
153             metaCache.setValue(name, Integer.valueOf(value));
154           } else if (long.class == type || Long.class == type) {
155             metaCache.setValue(name, Long.valueOf(value));
156           } else if (short.class == type || Short.class == type) {
157             metaCache.setValue(name, Short.valueOf(value));
158           } else if (byte.class == type || Byte.class == type) {
159             metaCache.setValue(name, Byte.valueOf(value));
160           } else if (float.class == type || Float.class == type) {
161             metaCache.setValue(name, Float.valueOf(value));
162           } else if (boolean.class == type || Boolean.class == type) {
163             metaCache.setValue(name, Boolean.valueOf(value));
164           } else if (double.class == type || Double.class == type) {
165             metaCache.setValue(name, Double.valueOf(value));
166           } else {
167             throw new CacheException("Unsupported property type for cache: '" + name + "' of type " + type);
168           }
169         }
170       }
171     }
172     if (InitializingObject.class.isAssignableFrom(cache.getClass())) {
173       try {
174         ((InitializingObject) cache).initialize();
175       } catch (Exception e) {
176         throw new CacheException(
177             "Failed cache initialization for '" + cache.getId() + "' on '" + cache.getClass().getName() + "'", e);
178       }
179     }
180   }
181 
182   private Cache newBaseCacheInstance(Class<? extends Cache> cacheClass, String id) {
183     Constructor<? extends Cache> cacheConstructor = getBaseCacheConstructor(cacheClass);
184     try {
185       return cacheConstructor.newInstance(id);
186     } catch (Exception e) {
187       throw new CacheException("Could not instantiate cache implementation (" + cacheClass + "). Cause: " + e, e);
188     }
189   }
190 
191   private Constructor<? extends Cache> getBaseCacheConstructor(Class<? extends Cache> cacheClass) {
192     try {
193       return cacheClass.getConstructor(String.class);
194     } catch (Exception e) {
195       throw new CacheException("Invalid base cache implementation (" + cacheClass + ").  "
196           + "Base cache implementations must have a constructor that takes a String id as a parameter.  Cause: " + e,
197           e);
198     }
199   }
200 
201   private Cache newCacheDecoratorInstance(Class<? extends Cache> cacheClass, Cache base) {
202     Constructor<? extends Cache> cacheConstructor = getCacheDecoratorConstructor(cacheClass);
203     try {
204       return cacheConstructor.newInstance(base);
205     } catch (Exception e) {
206       throw new CacheException("Could not instantiate cache decorator (" + cacheClass + "). Cause: " + e, e);
207     }
208   }
209 
210   private Constructor<? extends Cache> getCacheDecoratorConstructor(Class<? extends Cache> cacheClass) {
211     try {
212       return cacheClass.getConstructor(Cache.class);
213     } catch (Exception e) {
214       throw new CacheException("Invalid cache decorator (" + cacheClass + ").  "
215           + "Cache decorators must have a constructor that takes a Cache instance as a parameter.  Cause: " + e, e);
216     }
217   }
218 }