KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibatis > sqlmap > engine > cache > CacheModel


1 /*
2  * Copyright 2004 Clinton Begin
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 com.ibatis.sqlmap.engine.cache;
17
18 import com.ibatis.common.exception.NestedRuntimeException;
19 import com.ibatis.common.resources.Resources;
20 import com.ibatis.sqlmap.engine.mapping.statement.ExecuteListener;
21 import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
22
23 import java.io.*;
24 import java.util.*;
25
26 /**
27  *
28  */

29 public class CacheModel implements ExecuteListener {
30
31   private static final Map lockMap = new HashMap();
32   /**
33    * This is used to represent null objects that are returned from the cache so
34    * that they can be cached, too.
35    */

36   public static final Object JavaDoc NULL_OBJECT = new Object JavaDoc();
37   private final Object JavaDoc STATS_LOCK = new Object JavaDoc();
38   private int requests = 0;
39   private int hits = 0;
40
41   /**
42    * Constant to turn off periodic cache flushes
43    */

44   private static final long NO_FLUSH_INTERVAL = -99999;
45
46   private String JavaDoc id;
47
48   private boolean readOnly;
49   private boolean serialize;
50
51   private long lastFlush;
52   private long flushInterval;
53   private long flushIntervalSeconds;
54   private Set flushTriggerStatements;
55
56   private CacheController controller;
57
58   private String JavaDoc resource;
59
60   /**
61    * Default constructor
62    */

63   public CacheModel() {
64     this.flushInterval = NO_FLUSH_INTERVAL;
65     this.flushIntervalSeconds = NO_FLUSH_INTERVAL;
66     this.lastFlush = System.currentTimeMillis();
67     this.flushTriggerStatements = new HashSet();
68   }
69
70   /**
71    * Getter for the cache model's id
72    *
73    * @return the id
74    */

75   public String JavaDoc getId() {
76     return id;
77   }
78
79   /**
80    * Setter for the cache model's id
81    *
82    * @param id - the new id
83    */

84   public void setId(String JavaDoc id) {
85     this.id = id;
86   }
87
88   /**
89    * Getter for read-only property
90    *
91    * @return true if a read-only model
92    */

93   public boolean isReadOnly() {
94     return readOnly;
95   }
96
97   /**
98    * Setter for read-only property
99    *
100    * @param readOnly - the new setting
101    */

102   public void setReadOnly(boolean readOnly) {
103     this.readOnly = readOnly;
104   }
105
106   /**
107    * Getter to tell if the cache serializes
108    *
109    * @return true if the cache model serializes objects
110    */

111   public boolean isSerialize() {
112     return serialize;
113   }
114
115   /**
116    * Setter to tell the cache to serialize objects
117    *
118    * @param serialize - if the cache model is to serialize objects
119    */

120   public void setSerialize(boolean serialize) {
121     this.serialize = serialize;
122   }
123
124   /**
125    * Getter for resource property
126    *
127    * @return the value of the resource property
128    */

129   public String JavaDoc getResource() {
130     return resource;
131   }
132
133   /**
134    * Setter for resource property
135    *
136    * @param resource - the new value
137    */

138   public void setResource(String JavaDoc resource) {
139     this.resource = resource;
140   }
141
142   /**
143    * Sets up the controller for the cache model
144    *
145    * @param implementation - the class (FQCN) for the controller
146    * @throws ClassNotFoundException - if the class cannot be found
147    * @throws InstantiationException - if the class cannot be instantiated
148    * @throws IllegalAccessException - if the classes constructor is not accessible
149    */

150   public void setControllerClassName(String JavaDoc implementation)
151       throws ClassNotFoundException JavaDoc, InstantiationException JavaDoc, IllegalAccessException JavaDoc {
152     Class JavaDoc clazz = Resources.classForName(implementation);
153     controller = (CacheController) Resources.instantiate(clazz);
154   }
155
156   /**
157    * Getter for flushInterval property
158    *
159    * @return The flushInterval (in milliseconds)
160    */

161   public long getFlushInterval() {
162     return flushInterval;
163   }
164
165   /**
166    * Getter for flushInterval property
167    *
168    * @return The flushInterval (in milliseconds)
169    */

170   public long getFlushIntervalSeconds() {
171     return flushIntervalSeconds;
172   }
173
174   /**
175    * Setter for flushInterval property
176    *
177    * @param flushInterval The new flushInterval (in milliseconds)
178    */

179   public void setFlushInterval(long flushInterval) {
180     this.flushInterval = flushInterval;
181     this.flushIntervalSeconds = flushInterval / 1000;
182   }
183
184   /**
185    * Adds a flushTriggerStatment. When a flushTriggerStatment is executed, the
186    * cache is flushed (cleared).
187    *
188    * @param statementName The statement to add.
189    */

190   public void addFlushTriggerStatement(String JavaDoc statementName) {
191     flushTriggerStatements.add(statementName);
192   }
193
194   /**
195    * Gets an Iterator containing all flushTriggerStatment objects for this cache.
196    *
197    * @return The Iterator
198    */

199   public Iterator getFlushTriggerStatementNames() {
200     return flushTriggerStatements.iterator();
201   }
202
203   /**
204    * ExecuteListener event. This will be called by a MappedStatement
205    * for which this cache is registered as a ExecuteListener. It will
206    * be called each time an executeXXXXXX method is called. In the
207    * case of the Cache class, it is registered in order to flush the
208    * cache whenever a certain statement is executed.
209    * (i.e. the flushOnExecute cache policy)
210    *
211    * @param statement The statement to execute
212    */

213   public void onExecuteStatement(MappedStatement statement) {
214     flush();
215   }
216
217
218   /**
219    * Returns statistical information about the cache.
220    *
221    * @return the number of cache hits divided by the total requests
222    */

223   public double getHitRatio() {
224     return (double) hits / (double) requests;
225   }
226
227   /**
228    * Configures the cache
229    *
230    * @param props
231    */

232   public void configure(Properties props) {
233     controller.configure(props);
234   }
235
236   /**
237    * Clears the cache
238    */

239   public void flush() {
240     lastFlush = System.currentTimeMillis();
241     // use the controller's key
242
CacheKey key = new CacheKey();
243     key.update(controller);
244     synchronized (getLock(key)) {
245       controller.flush(this);
246     }
247   }
248
249   /**
250    * Get an object out of the cache.
251    * A side effect of this method is that is may clear the cache if it has not been
252    * cleared in the flushInterval.
253    *
254    * @param key The key of the object to be returned
255    * @return The cached object (or null)
256    */

257   public Object JavaDoc getObject(CacheKey key) {
258     synchronized (this) {
259       if (flushInterval != NO_FLUSH_INTERVAL
260           && System.currentTimeMillis() - lastFlush > flushInterval) {
261         flush();
262       }
263     }
264
265     Object JavaDoc value = null;
266     synchronized (getLock(key)) {
267       value = controller.getObject(this, key);
268     }
269
270     if (serialize && !readOnly && (value != NULL_OBJECT && value != null)) {
271       try {
272         ByteArrayInputStream bis = new ByteArrayInputStream((byte[]) value);
273         ObjectInputStream ois = new ObjectInputStream(bis);
274         value = ois.readObject();
275         ois.close();
276       } catch (Exception JavaDoc e) {
277         throw new NestedRuntimeException("Error caching serializable object. Be sure you're not attempting to use " +
278             "a serialized cache for an object that may be taking advantage of lazy loading. Cause: " + e, e);
279       }
280     }
281
282     synchronized (STATS_LOCK) {
283       requests++;
284       if (value != null) {
285         hits++;
286       }
287     }
288
289     return value == NULL_OBJECT ? null : value;
290
291   }
292
293   /**
294    * Add an object to the cache
295    *
296    * @param key The key of the object to be cached
297    * @param value The object to be cached
298    */

299   public void putObject(CacheKey key, Object JavaDoc value) {
300     if (null == value) value = NULL_OBJECT;
301     if (serialize && !readOnly && value != NULL_OBJECT) {
302       try {
303         ByteArrayOutputStream bos = new ByteArrayOutputStream();
304         ObjectOutputStream oos = new ObjectOutputStream(bos);
305         oos.writeObject(value);
306         oos.flush();
307         oos.close();
308         value = bos.toByteArray();
309       } catch (IOException e) {
310         throw new NestedRuntimeException("Error caching serializable object. Cause: " + e, e);
311       }
312     }
313     synchronized (getLock(key)) {
314       controller.putObject(this, key, value);
315     }
316   }
317
318   /**
319    * OK, honestly, i have no idea what this does.
320    * @param key
321    * @return
322    */

323   public synchronized final Object JavaDoc getLock(CacheKey key) {
324     int controllerId = System.identityHashCode(controller);
325     int keyHash = key.hashCode();
326     Integer JavaDoc lockKey = new Integer JavaDoc(29 * controllerId + keyHash);
327     Object JavaDoc lock = lockMap.get(lockKey);
328     if (lock == null) {
329       lock = lockKey; //might as well use the same object
330
lockMap.put(lockKey, lock);
331     }
332     return lock;
333   }
334
335 }
336
Popular Tags