View Javadoc

1   /*
2    * Copyright (c) 2004-2005, University Health Network.  All rights reserved. Distributed under the BSD 
3    * license (see http://opensource.org/licenses/bsd-license.php).
4    *  
5    * IChunkStore.java
6    *
7    * Created on 2-Dec-2004 at 5:29:25 PM
8    */
9   package ca.uhn.cache.internal.impl;
10  
11  import java.util.Arrays;
12  import java.util.Collection;
13  import java.util.Date;
14  import java.util.HashSet;
15  import java.util.Iterator;
16  import java.util.List;
17  import java.util.Set;
18  
19  import net.sf.hibernate.Hibernate;
20  
21  import org.apache.commons.collections.CollectionUtils;
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.springframework.beans.factory.InitializingBean;
25  import org.springframework.orm.hibernate.support.HibernateDaoSupport;
26  
27  import ca.uhn.cache.CacheReasonEnum;
28  import ca.uhn.cache.IQuery;
29  import ca.uhn.cache.VolatilityEnum;
30  import ca.uhn.cache.internal.IChunk;
31  import ca.uhn.cache.internal.IChunkIterator;
32  import ca.uhn.cache.internal.IChunkPurger;
33  import ca.uhn.cache.internal.IChunkStore;
34  import ca.uhn.cache.internal.IStaleChunkRule;
35  import ca.uhn.cache.internal.hibernate.impl.ChunkField;
36  import ca.uhn.cache.internal.util.TimestampUtils;
37  import ca.uhn.cache.internal.util.UTC;
38  
39  /***
40   * Hibernate-based implementation of <code>IChunkStore</code>
41   * 
42   * @author <a href="mailto:alexei.guevara@uhn.on.ca">Alexei Guevara</a>
43   * @version $Revision: 1.1 $ updated on $Date: 2005/01/24 22:54:04 $ by $Author: bryan_tripp $
44   */
45  public class HibernateChunkStore extends HibernateDaoSupport implements IChunkStore, IChunkPurger, InitializingBean {
46  
47      private static Log ourLog = LogFactory.getLog(HibernateChunkStore.class);
48      
49      private final IStaleChunkRule myStaleChunkRule;
50  
51      /***
52       * Constructor.
53       * 
54       * @param theStaleChunkRule The rule to decide when the chunks will become stale. 
55       */
56      public HibernateChunkStore( IStaleChunkRule theStaleChunkRule )
57      {
58          myStaleChunkRule = theStaleChunkRule;
59      }
60      
61      /***
62       * @param theQuery The specified query.
63       * @return The value to which this <code>IChunkStore</code> maps the specified query, or
64       *         <tt>null</tt> if the <code>IChunkStore</code> contains no mapping for the query. 
65       */
66      public IChunk get( IQuery theQuery )
67      {
68          assert theQuery != null;
69          assert !theQuery.isEmpty();
70  
71          IChunk retVal = null;
72  
73          List foundChunkList = null;
74  
75          //TODO: make table and field names so that they don't need to be hard coded
76          foundChunkList = getHibernateTemplate().find(
77                  "FROM " +ChunkField.class.getName()+ " WHERE queryHash=?",
78                  new Integer( theQuery.hashCode() ),
79                  Hibernate.INTEGER );
80          
81          if (!foundChunkList.isEmpty()){
82              ChunkField foundChunk = (ChunkField)foundChunkList.get(0);
83  
84              if (foundChunk != null){
85                  retVal = foundChunk.toChunk();
86              }
87          }
88          
89          return retVal;
90      }
91      
92  
93      /***
94       * Places a <code>IChunk</code> corresponding to the specified <code>IQuery</code>
95       * in this <code>IChunkStore</code>.  If the <code>IChunkStore</code> previously contained 
96       * a mapping for this query, the value is replaced by a new <code>IChunk</code> with the new volatility
97       * and reasons.
98       *  
99       * @param theQuery Query with which the chunk is to be associated.
100      * @param theVolatility The volatility that will decide the length of stay in the cache. If there
101      * is a mapping for this query currently in the cache, this will replace the old value. 
102      * @param theReasons The reasons that this chunk was cached. If there is a mapping for this query
103      * currently in the cache, this will be appended to the old value.
104      * 
105      * @return Previous value associated with specified query, or <tt>null</tt>
106      *         if there was no mapping for the query.
107      */
108     public IChunk put( IQuery theQuery, VolatilityEnum theVolatility, CacheReasonEnum[] theReasons )
109     {
110         assert theQuery != null;
111         assert !theQuery.isEmpty();
112         assert theVolatility != null;
113         assert theReasons != null;
114         assert theReasons.length > 0;
115         
116         IChunk retVal = null;
117         Set cacheReasons = new HashSet();
118 
119         // First check if this chunk already exists in the store
120         retVal = get(theQuery);
121 
122         if (retVal != null){
123             CacheReasonEnum[] oldReasons = retVal.getReasons();
124             Collection newReasons = CollectionUtils.union(Arrays.asList(oldReasons), Arrays.asList(theReasons));
125 
126             Iterator iter = newReasons.iterator();
127             while(iter.hasNext()){
128                 CacheReasonEnum next = (CacheReasonEnum)iter.next();
129                 cacheReasons.add( next );
130             }
131             
132             ChunkField newVal = new ChunkField();
133             newVal.setHibernateId(retVal.getId());
134 
135             newVal.setVolatility(theVolatility);
136             newVal.setLastUpdateTime( TimestampUtils.dateToTimestamp( UTC.currentTime() ) );
137             newVal.setCacheTime( TimestampUtils.dateToTimestamp( retVal.getCacheTime() ) );
138             newVal.setLastAccessTime( TimestampUtils.dateToTimestamp( UTC.currentTime() ) );
139             newVal.setReasons( cacheReasons );
140             newVal.setQueryHash( theQuery.hashCode() );
141 
142             getHibernateTemplate().update(newVal);
143         }
144         else{
145             ChunkField newVal = new ChunkField();
146 
147             for (int i = 0; i < theReasons.length; i++){
148                 cacheReasons.add( theReasons[ i ] );
149             }
150             
151             newVal.setVolatility(theVolatility);
152             newVal.setLastUpdateTime(TimestampUtils.dateToTimestamp( UTC.currentTime() ));
153             newVal.setCacheTime(TimestampUtils.dateToTimestamp( UTC.currentTime() ));
154             newVal.setLastAccessTime(TimestampUtils.dateToTimestamp( UTC.currentTime() ));
155             newVal.setReasons(cacheReasons);
156             newVal.setQueryHash(theQuery.hashCode());
157             
158             //set expity date/time
159             long maxAgeInMillis = getStaleChunkRule().getMaxAge( newVal.toChunk() );
160             Date expiryDate = UTC.fromCurrentTime( maxAgeInMillis );
161             newVal.setExpiryTime( TimestampUtils.dateToTimestamp( expiryDate ) );
162             
163             getHibernateTemplate().save(newVal);
164         }
165         
166         return retVal;
167     }
168     
169     /***
170      * Removes the mapping for this query from this <code>IChunkStore</code> if it is present 
171      * 
172      * @param theQuery Query whose mapping is to be removed from the <code>IChunkStore</code>.
173      * 
174      * @return Previous value associated with specified query, or <tt>null</tt>
175      *         if there was no mapping for the query.
176      */
177     public IChunk remove( IQuery theQuery )
178     {
179         assert theQuery != null;
180         assert !theQuery.isEmpty();
181 
182         IChunk retVal = get(theQuery);
183         
184         if (retVal != null){
185             getHibernateTemplate().delete(
186                     "FROM " +ChunkField.class.getName()+ " WHERE queryHash=?",
187                     new Integer( theQuery.hashCode() ),
188                     Hibernate.INTEGER );
189         }
190         
191         return retVal;
192     }
193 
194     /***
195      * {@inheritDoc}
196      */
197     public IChunkIterator getUnusedChunks( long theVoguenessThreshold ) {
198         //TODO implement getUnusedChunks
199         throw new UnsupportedOperationException( "Not implemented" );
200     }
201 
202     /***
203      * {@inheritDoc}
204      */
205     public IChunkIterator getStaleChunks() {
206         ChunkIterator retVal = new ChunkIterator();
207         
208         List staleChunkList = 
209             getHibernateTemplate().find(
210                     "FROM " +ChunkField.class.getName()+ " WHERE expiryTime <= ?", 
211                     TimestampUtils.dateToTimestamp( UTC.currentTime() ), 
212                     Hibernate.TIMESTAMP );
213         
214         for (Iterator iter = staleChunkList.iterator(); iter.hasNext();) {
215             ChunkField chunk = (ChunkField) iter.next();
216             retVal.add( chunk.toChunk() );
217         }
218         
219         retVal.finished();
220         
221         return retVal;
222         
223     }
224 
225     /***
226      * {@inheritDoc}
227      */
228     public long getLowestVogueness() {
229         //TODO implement getLowestVogueness
230         throw new UnsupportedOperationException( "Not implemented" );
231     }
232 
233     /***
234      * {@inheritDoc}
235      */
236     public int getNumCachedItems() {
237         //TODO implement getNumCachedItems
238         throw new UnsupportedOperationException( "Not implemented" );
239     }
240 
241     /***
242      * {@inheritDoc}
243      */
244     public IStaleChunkRule getStaleChunkRule() {
245         return myStaleChunkRule;
246     }
247     
248 }