View Javadoc

1   /* Copyright 2006 The JA-SIG Collaborative.  All rights reserved.
2   *  See license distributed with this file and
3   *  available online at http://www.uportal.org/license.html
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"); //TODO upgrade spring to get rid of this line
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 }