View Javadoc
1   /*
2    *    Copyright 2009-2021 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    *       http://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
153               || Integer.class == type) {
154             metaCache.setValue(name, Integer.valueOf(value));
155           } else if (long.class == type
156               || Long.class == type) {
157             metaCache.setValue(name, Long.valueOf(value));
158           } else if (short.class == type
159               || Short.class == type) {
160             metaCache.setValue(name, Short.valueOf(value));
161           } else if (byte.class == type
162               || Byte.class == type) {
163             metaCache.setValue(name, Byte.valueOf(value));
164           } else if (float.class == type
165               || Float.class == type) {
166             metaCache.setValue(name, Float.valueOf(value));
167           } else if (boolean.class == type
168               || Boolean.class == type) {
169             metaCache.setValue(name, Boolean.valueOf(value));
170           } else if (double.class == type
171               || Double.class == type) {
172             metaCache.setValue(name, Double.valueOf(value));
173           } else {
174             throw new CacheException("Unsupported property type for cache: '" + name + "' of type " + type);
175           }
176         }
177       }
178     }
179     if (InitializingObject.class.isAssignableFrom(cache.getClass())) {
180       try {
181         ((InitializingObject) cache).initialize();
182       } catch (Exception e) {
183         throw new CacheException("Failed cache initialization for '"
184           + cache.getId() + "' on '" + cache.getClass().getName() + "'", e);
185       }
186     }
187   }
188 
189   private Cache newBaseCacheInstance(Class<? extends Cache> cacheClass, String id) {
190     Constructor<? extends Cache> cacheConstructor = getBaseCacheConstructor(cacheClass);
191     try {
192       return cacheConstructor.newInstance(id);
193     } catch (Exception e) {
194       throw new CacheException("Could not instantiate cache implementation (" + cacheClass + "). Cause: " + e, e);
195     }
196   }
197 
198   private Constructor<? extends Cache> getBaseCacheConstructor(Class<? extends Cache> cacheClass) {
199     try {
200       return cacheClass.getConstructor(String.class);
201     } catch (Exception e) {
202       throw new CacheException("Invalid base cache implementation (" + cacheClass + ").  "
203         + "Base cache implementations must have a constructor that takes a String id as a parameter.  Cause: " + e, e);
204     }
205   }
206 
207   private Cache newCacheDecoratorInstance(Class<? extends Cache> cacheClass, Cache base) {
208     Constructor<? extends Cache> cacheConstructor = getCacheDecoratorConstructor(cacheClass);
209     try {
210       return cacheConstructor.newInstance(base);
211     } catch (Exception e) {
212       throw new CacheException("Could not instantiate cache decorator (" + cacheClass + "). Cause: " + e, e);
213     }
214   }
215 
216   private Constructor<? extends Cache> getCacheDecoratorConstructor(Class<? extends Cache> cacheClass) {
217     try {
218       return cacheClass.getConstructor(Cache.class);
219     } catch (Exception e) {
220       throw new CacheException("Invalid cache decorator (" + cacheClass + ").  "
221         + "Cache decorators must have a constructor that takes a Cache instance as a parameter.  Cause: " + e, e);
222     }
223   }
224 }