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