1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.ibatis.sqlmap.engine.cache;
17
18 import com.ibatis.common.logging.Log;
19 import com.ibatis.common.logging.LogFactory;
20 import com.ibatis.sqlmap.engine.mapping.statement.ExecuteListener;
21 import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
22
23 import java.io.ByteArrayInputStream;
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutputStream;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.Properties;
31 import java.util.Set;
32
33
34
35
36 public class CacheModel implements ExecuteListener {
37
38
39 private static final Log log = LogFactory.getLog(CacheModel.class);
40
41
42 private static final int MAX_OBJECT_LOG_SIZE = 200;
43
44
45
46
47 public static final Object NULL_OBJECT = "SERIALIZABLE_NULL_OBJECT";
48
49
50 private int requests = 0;
51
52
53 private int hits = 0;
54
55
56 private static final long NO_FLUSH_INTERVAL = -99999;
57
58
59 private String id;
60
61
62 private boolean readOnly;
63
64
65 private boolean serialize;
66
67
68 private long lastFlush;
69
70
71 private long flushInterval;
72
73
74 private long flushIntervalSeconds;
75
76
77 private Set flushTriggerStatements;
78
79
80 private CacheController controller;
81
82
83 private String resource;
84
85
86
87
88 public CacheModel() {
89 this.flushInterval = NO_FLUSH_INTERVAL;
90 this.flushIntervalSeconds = NO_FLUSH_INTERVAL;
91 this.lastFlush = System.currentTimeMillis();
92 this.flushTriggerStatements = new HashSet<>();
93 }
94
95
96
97
98
99
100 public String getId() {
101 return id;
102 }
103
104
105
106
107
108
109
110 public void setId(String id) {
111 this.id = id;
112 }
113
114
115
116
117
118
119 public boolean isReadOnly() {
120 return readOnly;
121 }
122
123
124
125
126
127
128
129 public void setReadOnly(boolean readOnly) {
130 this.readOnly = readOnly;
131 }
132
133
134
135
136
137
138 public boolean isSerialize() {
139 return serialize;
140 }
141
142
143
144
145
146
147
148 public void setSerialize(boolean serialize) {
149 this.serialize = serialize;
150 }
151
152
153
154
155
156
157 public String getResource() {
158 return resource;
159 }
160
161
162
163
164
165
166
167 public void setResource(String resource) {
168 this.resource = resource;
169 }
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 public void setCacheController(CacheController controller)
185 throws ClassNotFoundException, InstantiationException, IllegalAccessException {
186 this.controller = controller;
187 }
188
189
190
191
192
193
194 public long getFlushInterval() {
195 return flushInterval;
196 }
197
198
199
200
201
202
203 public long getFlushIntervalSeconds() {
204 return flushIntervalSeconds;
205 }
206
207
208
209
210
211
212
213 public void setFlushInterval(long flushInterval) {
214 this.flushInterval = flushInterval;
215 this.flushIntervalSeconds = flushInterval / 1000;
216 }
217
218
219
220
221
222
223
224 public void addFlushTriggerStatement(String statementName) {
225 flushTriggerStatements.add(statementName);
226 }
227
228
229
230
231
232
233 public Iterator getFlushTriggerStatementNames() {
234 return flushTriggerStatements.iterator();
235 }
236
237
238
239
240
241
242
243
244
245
246 @Override
247 public void onExecuteStatement(MappedStatement statement) {
248 flush();
249 }
250
251
252
253
254
255
256 public double getHitRatio() {
257 return (double) hits / (double) requests;
258 }
259
260
261
262
263
264
265
266 public void configure(Properties props) {
267 controller.setProperties(props);
268 }
269
270
271
272
273 public void flush() {
274 synchronized (this) {
275 controller.flush(this);
276 lastFlush = System.currentTimeMillis();
277 if (log.isDebugEnabled()) {
278 log("flushed", false, null);
279 }
280 }
281 }
282
283
284
285
286
287
288
289
290
291
292 public Object getObject(CacheKey key) {
293 Object value = null;
294 synchronized (this) {
295 if (flushInterval != NO_FLUSH_INTERVAL && System.currentTimeMillis() - lastFlush > flushInterval) {
296 flush();
297 }
298
299 value = controller.getObject(this, key);
300 if (serialize && !readOnly && value != NULL_OBJECT && value != null) {
301 try {
302 ByteArrayInputStream bis = new ByteArrayInputStream((byte[]) value);
303 ObjectInputStream ois = new ObjectInputStream(bis);
304 value = ois.readObject();
305 ois.close();
306 } catch (Exception e) {
307 throw new RuntimeException("Error caching serializable object. Be sure you're not attempting to use "
308 + "a serialized cache for an object that may be taking advantage of lazy loading. Cause: " + e, e);
309 }
310 }
311 requests++;
312 if (value != null) {
313 hits++;
314 }
315 if (log.isDebugEnabled()) {
316 if (value != null) {
317 log("retrieved object", true, value);
318 } else {
319 log("cache miss", false, null);
320 }
321 }
322 }
323 return value;
324 }
325
326
327
328
329
330
331
332
333
334 public void putObject(CacheKey key, Object value) {
335 if (null == value) {
336 value = NULL_OBJECT;
337 }
338 synchronized (this) {
339 if (serialize && !readOnly && value != NULL_OBJECT) {
340 try {
341 ByteArrayOutputStream bos = new ByteArrayOutputStream();
342 ObjectOutputStream oos = new ObjectOutputStream(bos);
343 oos.writeObject(value);
344 oos.flush();
345 oos.close();
346 value = bos.toByteArray();
347 } catch (IOException e) {
348 throw new RuntimeException("Error caching serializable object. Cause: " + e, e);
349 }
350 }
351 controller.putObject(this, key, value);
352 if (log.isDebugEnabled()) {
353 log("stored object", true, value);
354 }
355 }
356 }
357
358
359
360
361
362
363 protected int getMaxObjectLogSize() {
364 return MAX_OBJECT_LOG_SIZE;
365 }
366
367
368
369
370
371
372
373
374
375
376
377
378 protected void log(String action, boolean addValue, Object cacheValue) {
379 StringBuilder output = new StringBuilder("Cache '");
380 output.append(getId());
381 output.append("': ");
382 output.append(action);
383 if (addValue) {
384 String cacheObjectStr = cacheValue == null ? "null" : cacheValue.toString();
385 output.append(" '");
386 if (cacheObjectStr.length() < getMaxObjectLogSize()) {
387 output.append(cacheObjectStr);
388 } else {
389 output.append(cacheObjectStr.substring(1, getMaxObjectLogSize()));
390 output.append("...");
391 }
392 output.append("'");
393 }
394 log.debug(output.toString());
395 }
396
397
398
399
400
401
402
403 public void setControllerProperties(Properties cacheProps) {
404 controller.setProperties(cacheProps);
405 }
406 }