1
2
3
4
5
6 package edu.wisc.my.webproxy.beans.cache.oscache;
7
8 import java.io.Serializable;
9 import java.sql.ResultSet;
10 import java.sql.SQLException;
11 import java.sql.Types;
12 import java.util.List;
13 import java.util.Set;
14
15 import javax.sql.DataSource;
16
17 import org.apache.commons.lang.SerializationException;
18 import org.apache.commons.lang.SerializationUtils;
19 import org.springframework.dao.support.DataAccessUtils;
20 import org.springframework.jdbc.core.SqlParameter;
21 import org.springframework.jdbc.core.support.JdbcDaoSupport;
22 import org.springframework.jdbc.core.support.SqlLobValue;
23 import org.springframework.jdbc.object.MappingSqlQuery;
24 import org.springframework.jdbc.object.SqlUpdate;
25 import org.springframework.jdbc.support.lob.LobHandler;
26
27 import com.opensymphony.oscache.base.Config;
28 import com.opensymphony.oscache.base.persistence.CachePersistenceException;
29 import com.opensymphony.oscache.base.persistence.PersistenceListener;
30
31 /***
32 * Expects a table named WP_CACHE_STORE with three fields. CACHE_KEY is a VARCHAR(2000), OBJECT_TYPE is a VARCHAR(1), CACHE_OBJECT is a BLOB.
33 * CACHE_KEY & OBJECT_TYPE make up the primary key.
34 *
35 * @author Eric Dalquist <a href="mailto:eric.dalquist@doit.wisc.edu">eric.dalquist@doit.wisc.edu</a>
36 * @version $Revision: 1.1 $
37 */
38 public class JdbcPersistenceListener extends JdbcDaoSupport implements PersistenceListener {
39 private ObjectExistsQuery objectExistsQuery;
40 private AllObjectsDelete allObjectsDelete;
41 private ObjectDelete objectDelete;
42 private ObjectQuery objectQuery;
43 private ObjectInsert objectInsert;
44 private ObjectUpdate objectUpdate;
45
46 private LobHandler lobHandler;
47
48
49 /***
50 * @return Returns the lobHandler.
51 */
52 public LobHandler getLobHandler() {
53 return this.lobHandler;
54 }
55 /***
56 * @param lobHandler The lobHandler to set.
57 */
58 public void setLobHandler(LobHandler lobHandler) {
59 this.lobHandler = lobHandler;
60 }
61
62 /***
63 * @see org.springframework.jdbc.core.support.JdbcDaoSupport#initDao()
64 */
65 protected void initDao() throws Exception {
66 this.objectExistsQuery = new ObjectExistsQuery(this.getDataSource());
67 this.allObjectsDelete = new AllObjectsDelete(this.getDataSource());
68 this.objectDelete = new ObjectDelete(this.getDataSource());
69 this.objectQuery = new ObjectQuery(this.getDataSource());
70 this.objectInsert = new ObjectInsert(this.getDataSource());
71 this.objectUpdate = new ObjectUpdate(this.getDataSource());
72 }
73
74 /***
75 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#isStored(java.lang.String)
76 */
77 public boolean isStored(String key) throws CachePersistenceException {
78 return this.isStored(key, StoredObjectType.OBJECT);
79 }
80
81 /***
82 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#isGroupStored(java.lang.String)
83 */
84 public boolean isGroupStored(String groupName) throws CachePersistenceException {
85 return this.isStored(groupName, StoredObjectType.GROUP);
86 }
87
88 /***
89 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#clear()
90 */
91 public void clear() throws CachePersistenceException {
92 this.allObjectsDelete.update();
93 }
94
95 /***
96 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#configure(com.opensymphony.oscache.base.Config)
97 */
98 public PersistenceListener configure(Config config) {
99 return this;
100 }
101
102 /***
103 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#remove(java.lang.String)
104 */
105 public void remove(String key) throws CachePersistenceException {
106 this.remove(key, StoredObjectType.OBJECT);
107 }
108
109 /***
110 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#removeGroup(java.lang.String)
111 */
112 public void removeGroup(String groupName) throws CachePersistenceException {
113 this.remove(groupName, StoredObjectType.GROUP);
114 }
115
116 /***
117 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#retrieve(java.lang.String)
118 */
119 public Object retrieve(String key) throws CachePersistenceException {
120 return this.retrieve(key, StoredObjectType.OBJECT);
121 }
122
123 /***
124 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#retrieveGroup(java.lang.String)
125 */
126 public Set retrieveGroup(String groupName) throws CachePersistenceException {
127 return (Set)this.retrieve(groupName, StoredObjectType.GROUP);
128 }
129
130 /***
131 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#store(java.lang.String, java.lang.Object)
132 */
133 public void store(String key, Object obj) throws CachePersistenceException {
134 this.store(key, StoredObjectType.OBJECT, obj);
135
136 }
137
138 /***
139 * @see com.opensymphony.oscache.base.persistence.PersistenceListener#storeGroup(java.lang.String, java.util.Set)
140 */
141 public void storeGroup(String groupName, Set keys) throws CachePersistenceException {
142 this.store(groupName, StoredObjectType.GROUP, keys);
143 }
144
145
146 //****************
147 //
148 // Typed methods shared by the Object and Group persistence methods
149 //
150 //***************
151
152
153 protected boolean isStored(String key, StoredObjectType type) {
154 final Object[] args = {key, type.toString()};
155 final List results = this.objectExistsQuery.execute(args);
156
157 return results.size() > 0;
158 }
159
160 protected void remove(String key, StoredObjectType type) {
161 final Object[] args = {key, type.toString()};
162 final int rowsDeleted = this.objectDelete.update(args);
163
164 if (rowsDeleted != 1) {
165 this.logger.warn("Deleting Object of type='" + type + "' for key='" + key + "' resulted in " + rowsDeleted + " rows deleted. Expected 1 row deleted.");
166 }
167 }
168
169 protected Object retrieve(String key, StoredObjectType type) {
170 final Object[] args = {key, type.toString()};
171 final List results = this.objectQuery.execute(args);
172
173 final byte[] serializedObject = (byte[])DataAccessUtils.uniqueResult(results);
174
175 if (serializedObject == null) {
176 return null;
177 }
178
179 final Object obj;
180
181 try {
182 obj = SerializationUtils.deserialize(serializedObject);
183 }
184 catch (SerializationException se) {
185 this.logger.error("Could not deserialize Object for key='" + key + "' and type='" + type + "'", se);
186 return null;
187 }
188
189 return obj;
190 }
191
192 protected void store(String key, StoredObjectType type, Object obj) {
193 final byte[] serializedObject;
194 if (obj != null) {
195 try {
196 serializedObject = SerializationUtils.serialize((Serializable)obj);
197 }
198 catch (SerializationException se) {
199 this.logger.error("Could not serialize Object='" + obj + "' for key='" + key + "' and type='" + type + "'", se);
200 return;
201 }
202 }
203 else {
204 serializedObject = null;
205 }
206
207 //Check to see if the Object exists in the DB already
208 final boolean exists = this.isStored(key, type);
209
210 //Does not exist, do an insert
211 if (!exists) {
212 final Object[] args = {
213 key,
214 type.toString(),
215 new SqlLobValue(serializedObject, this.lobHandler)};
216
217 final int rowsCreated = this.objectInsert.update(args);
218
219 if (rowsCreated != 1) {
220 this.logger.warn("Inserting Object='" + obj + "' for key='" + key + "' and type='" + type + "' resulted in " + rowsCreated + " rows created. Expected 1 row created.");
221 }
222 }
223 //Exists, update the row
224 else {
225 final Object[] args = {
226 new SqlLobValue(serializedObject, this.lobHandler),
227 key,
228 type.toString()};
229
230 final int rowsUpdated = this.objectUpdate.update(args);
231
232 if (rowsUpdated != 1) {
233 this.logger.warn("Updating Object='" + obj + "' for key='" + key + "' and type='" + type + "' resulted in " + rowsUpdated + " rows updated. Expected 1 row updated.");
234 }
235 }
236 }
237
238
239
240
241
242 /**
243 * Strongly typed enumeration for use with typed storage methods
244 */
245 public static class StoredObjectType {
246 public static final StoredObjectType OBJECT = new StoredObjectType('O');
247 public static final StoredObjectType GROUP = new StoredObjectType('G');
248
249 private final char name;
250 private final Character nameObj;
251
252 private StoredObjectType(char typeName) {
253 this.name = typeName;
254 this.nameObj = new Character(this.name);
255 }
256
257 /***
258 * @see java.lang.Object#equals(java.lang.Object)
259 */
260 public boolean equals(Object obj) {
261 if (this == obj) {
262 return true;
263 }
264 if (!(obj instanceof StoredObjectType)) {
265 return false;
266 }
267
268 final StoredObjectType other = (StoredObjectType)obj;
269
270 return this.name == other.name;
271 }
272
273 /***
274 * @see java.lang.Object#hashCode()
275 */
276 public int hashCode() {
277 return this.nameObj.hashCode();
278 }
279
280 /***
281 * @see java.lang.Object#toString()
282 */
283 public String toString() {
284 return this.nameObj.toString();
285 }
286 }
287
288
289
290 //****************
291 //
292 // Spring Query classes
293 //
294 //***************
295
296 private class ObjectExistsQuery extends MappingSqlQuery {
297 private static final String SQL =
298 "SELECT CACHE_KEY " +
299 "FROM WP_CACHE_STORE " +
300 "WHERE CACHE_KEY = ? and OBJECT_TYPE = ?";
301
302 public ObjectExistsQuery(DataSource ds) {
303 super(ds, SQL);
304
305 this.declareParameter(new SqlParameter("CACHE_KEY", Types.VARCHAR));
306 this.declareParameter(new SqlParameter("OBJECT_TYPE", Types.CHAR));
307
308 this.compile();
309 }
310
311 /**
312 * @see org.springframework.jdbc.object.MappingSqlQuery#mapRow(java.sql.ResultSet, int)
313 */
314 protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
315 return rs.getString("CACHE_KEY");
316 }
317 }
318
319
320 private class AllObjectsDelete extends SqlUpdate {
321 private static final String SQL =
322 "DELETE FROM WP_CACHE_STORE";
323
324 public AllObjectsDelete(DataSource ds) {
325 super(ds, SQL);
326
327 this.compile();
328 }
329 }
330
331
332 private class ObjectDelete extends SqlUpdate {
333 private static final String SQL =
334 "DELETE FROM WP_CACHE_STORE " +
335 "WHERE CACHE_KEY = ? and OBJECT_TYPE = ?";
336
337 public ObjectDelete(DataSource ds) {
338 super(ds, SQL);
339
340 this.declareParameter(new SqlParameter("CACHE_KEY", Types.VARCHAR));
341 this.declareParameter(new SqlParameter("OBJECT_TYPE", Types.CHAR));
342
343 this.compile();
344 }
345 }
346
347
348 private class ObjectQuery extends MappingSqlQuery {
349 private static final String SQL =
350 "SELECT CACHE_OBJECT " +
351 "FROM WP_CACHE_STORE " +
352 "WHERE CACHE_KEY = ? and OBJECT_TYPE = ?";
353
354 public ObjectQuery(DataSource ds) {
355 super(ds, SQL);
356
357 this.declareParameter(new SqlParameter("CACHE_KEY", Types.VARCHAR));
358 this.declareParameter(new SqlParameter("OBJECT_TYPE", Types.CHAR));
359
360 this.compile();
361 }
362
363 /***
364 * @see org.springframework.jdbc.object.MappingSqlQuery#mapRow(java.sql.ResultSet, int)
365 */
366 protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
367 final int colIndex = rs.findColumn("CACHE_OBJECT");
368 final byte[] serializedObject = JdbcPersistenceListener.this.lobHandler.getBlobAsBytes(rs, colIndex);
369
370 return serializedObject;
371 }
372 }
373
374
375 private class ObjectInsert extends SqlUpdate {
376 private static final String SQL =
377 "INSERT INTO WP_CACHE_STORE (CACHE_KEY, OBJECT_TYPE, CACHE_OBJECT) " +
378 "VALUES (?, ?, ?)";
379
380 public ObjectInsert(DataSource ds) {
381 super(ds, SQL);
382
383 this.declareParameter(new SqlParameter("CACHE_KEY", Types.VARCHAR));
384 this.declareParameter(new SqlParameter("OBJECT_TYPE", Types.CHAR));
385 this.declareParameter(new SqlParameter("CACHE_OBJECT", Types.BLOB));
386
387 this.compile();
388 }
389 }
390
391
392 private class ObjectUpdate extends SqlUpdate {
393 private static final String SQL =
394 "UPDATE WP_CACHE_STORE " +
395 "SET CACHE_OBJECT = ? " +
396 "WHERE CACHE_KEY = ? and OBJECT_TYPE = ?";
397
398 public ObjectUpdate(DataSource ds) {
399 super(ds, SQL);
400
401 this.declareParameter(new SqlParameter("CACHE_OBJECT", Types.BLOB));
402 this.declareParameter(new SqlParameter("CACHE_KEY", Types.VARCHAR));
403 this.declareParameter(new SqlParameter("OBJECT_TYPE", Types.CHAR));
404
405 this.compile();
406 }
407 }
408 }