View Javadoc

1   /*
2    * Copyright 2004-2005, University Health Network.  All rights reserved. Distributed under the BSD 
3    * license (see http://opensource.org/licenses/bsd-license.php).
4    *  
5    * ChunkBasedSemanticCache.java
6    *
7    * Created on 9-Dec-2004 at 4:16:37 PM
8    */
9   package ca.uhn.cache.impl;
10  
11  import java.util.ArrayList;
12  import java.util.Collection;
13  import java.util.Date;
14  import java.util.Iterator;
15  import java.util.List;
16  import java.util.Set;
17  
18  import jdsl.core.api.Position;
19  import jdsl.core.api.Tree;
20  import jdsl.core.ref.NodeTree;
21  
22  import org.apache.commons.collections.CollectionUtils;
23  
24  import ca.uhn.cache.CacheReasonEnum;
25  import ca.uhn.cache.IParamSpace;
26  import ca.uhn.cache.IQuery;
27  import ca.uhn.cache.IQueryParam;
28  import ca.uhn.cache.IQueryResult;
29  import ca.uhn.cache.ISemanticCache;
30  import ca.uhn.cache.VolatilityEnum;
31  import ca.uhn.cache.exception.CacheException;
32  import ca.uhn.cache.exception.QueryResultRetrievalCacheException;
33  import ca.uhn.cache.exception.QueryResultStorageCacheException;
34  import ca.uhn.cache.internal.IChunk;
35  import ca.uhn.cache.internal.IChunkStore;
36  import ca.uhn.cache.internal.IQueryResultStore;
37  import ca.uhn.cache.internal.exception.QueryResultStoreException;
38  import ca.uhn.cache.util.QueryUtil;
39  
40  /***
41   * Semantic cache implementation that uses the chunk approach described in
42   * 
43   * @inproceedings{ deshpande98caching, author = "Prasad M. Deshpande and
44   *                 Karthikeyan Ramasamy and Amit Shukla and Jeffrey F.
45   *                 Naughton", title = "Caching multidimensional queries using
46   *                 chunks", pages = "259--270", year = "1998", url =
47   *                 "citeseer.ist.psu.edu/deshpande98caching.html" }
48   * 
49   * @author <a href="mailto:alexei.guevara@uhn.on.ca">Alexei Guevara </a>
50   * @version $Revision: 1.1 $ updated on $Date: 2005/01/24 22:52:33 $ by $Author: bryan_tripp $
51   */
52  public class ChunkBasedSemanticCache implements ISemanticCache {
53  
54      private IParamSpace myParamSpace;
55      private IChunkStore myChunkStore;
56      private IQueryResultStore myQueryResultStore;
57      
58      /***
59       * 
60       */
61      public ChunkBasedSemanticCache() {
62      }
63  
64      /***
65       * {@inheritDoc}
66       */
67      public IQuery[] remainder( IQuery theQuery, int theMaxGroups ) throws CacheException {
68          assert theQuery != null;
69          assert theMaxGroups >= 0;
70          
71          IQuery[] retVal = null;
72          
73          //TODO add docs
74          
75          if ( theQuery.isEmpty() ) {
76              retVal = new IQuery[] {};
77          }
78          else {
79              List chunkEdges = new ArrayList();
80  
81              Set queryParams = theQuery.getParameters();
82              for (Iterator queryParamsIter = queryParams.iterator(); queryParamsIter.hasNext();) {
83                  IQueryParam queryParam = (IQueryParam) queryParamsIter.next();
84                  IQueryParam[] edgesOnThisDimension = getParamSpace().chunk( queryParam ); 
85                  chunkEdges.add( edgesOnThisDimension );
86              }
87  
88              int maxParamsPerQuery = chunkEdges.size();
89              
90              Tree queryParamsTree = new NodeTree();
91              
92              Collection leafs = CollectionUtils.typedCollection( new ArrayList(), Position.class );
93              leafs.add( queryParamsTree.root() );
94              
95              Collection newLeafs = CollectionUtils.typedCollection( new ArrayList(), Position.class );            
96              
97              for (Iterator chunkEdgesIter = chunkEdges.iterator(); chunkEdgesIter.hasNext();) {
98                  IQueryParam[] chunkEdgesOnDimension = (IQueryParam[]) chunkEdgesIter.next();
99                  
100                 for (int i = 0; i < chunkEdgesOnDimension.length; i++) {
101                     IQueryParam queryParam = chunkEdgesOnDimension[i];
102                     
103                     for (Iterator leafsIter = leafs.iterator(); leafsIter.hasNext();) {
104                         Position leaf = (Position) leafsIter.next();
105                         Position newLeaf = queryParamsTree.insertLastChild( leaf, queryParam );
106                         newLeafs.add( newLeaf );
107                     }
108                     
109                 }
110 
111                 leafs.clear();
112                 Collection tmp = leafs;
113                 leafs = newLeafs;
114                 newLeafs = tmp;
115                 
116             }
117             
118             List nonCachedChunkBoundaries = new ArrayList();
119             
120             for (Iterator leafsIter = leafs.iterator(); leafsIter.hasNext();) {
121                 Position leaf = (Position) leafsIter.next();
122 
123                 IQuery query = new Query();
124                 query.addParameter( (IQueryParam) leaf.element() );
125                 
126                 Position parent = queryParamsTree.parent(leaf);
127                 while ( parent != queryParamsTree.root() ) {
128                     query.addParameter( (IQueryParam) parent.element() );
129                     parent = queryParamsTree.parent( parent );
130                 }
131             
132                 IChunk chunk = getChunkStore().get( query );
133                 if ( chunk == null ) {
134                     nonCachedChunkBoundaries.add( query );
135                 }
136             }
137 
138             if ( theMaxGroups != 0 ) {
139                 retVal = getParamSpace().group(
140                         (IQuery[]) nonCachedChunkBoundaries.toArray( new IQuery[ nonCachedChunkBoundaries.size() ] ), 
141                         theMaxGroups );
142             }
143             else {
144                 retVal = (IQuery[]) nonCachedChunkBoundaries.toArray( new IQuery[ nonCachedChunkBoundaries.size() ] );
145             }
146         }
147         
148         return retVal;
149     }
150 
151     /***
152      * {@inheritDoc}
153      */
154     public IQueryResult get( IQuery theQuery ) throws CacheException {
155         IQueryResult retVal = null;
156         
157         try {
158             retVal = getQueryResultStore().select( theQuery );
159         }
160         catch (QueryResultStoreException e) {
161             throw new QueryResultRetrievalCacheException( e, theQuery ); 
162         }
163         
164         return retVal; 
165     }
166 
167     /***
168      * {@inheritDoc}
169      */
170     public void put( IQuery theQueryScope, IQueryResult theResult ) throws CacheException {
171         try {
172             //TODO the query store and the query result store must be transactionals resources.
173             getChunkStore().put( 
174                     theQueryScope, 
175                     VolatilityEnum.STABLE, new CacheReasonEnum[] { CacheReasonEnum.QUERY } );
176             
177             getQueryResultStore().insert( theResult );
178         }
179         catch (QueryResultStoreException e) {
180             throw new QueryResultStorageCacheException( e, theQueryScope, theResult ); 
181         }
182     }
183 
184     /***
185      * {@inheritDoc}
186      */
187     public void update( IQueryResult theResult ) throws CacheException {
188         try {
189             //TODO the query store and the query result store must be transactionals resources.
190             getQueryResultStore().insert( theResult );
191         }
192         catch (QueryResultStoreException e) {
193             throw new QueryResultStorageCacheException( "Error while updating a query result", e, null, theResult ); 
194         }
195     }
196     
197     /***
198      * @return Returns the paramSpace.
199      */
200     public IParamSpace getParamSpace() {
201         return myParamSpace;
202     }
203 
204     /***
205      * @param theParamSpace
206      *            The paramSpace to set.
207      */
208     public void setParamSpace( IParamSpace theParamSpace ) {
209         myParamSpace = theParamSpace;
210     }
211     
212     /***
213      * @return Returns the chunkStore.
214      */
215     public IChunkStore getChunkStore() {
216         return myChunkStore;
217     }
218 
219     /***
220      * @param theChunkStore The chunkStore to set.
221      */
222     public void setChunkStore( IChunkStore theChunkStore ) {
223         myChunkStore = theChunkStore;
224     }
225 
226     /***
227      * @return Returns the queryResultStore.
228      */
229     public IQueryResultStore getQueryResultStore() {
230         return myQueryResultStore;
231     }
232 
233     /***
234      * @param theQueryResultStore The queryResultStore to set.
235      */
236     public void setQueryResultStore( IQueryResultStore theQueryResultStore ) {
237         myQueryResultStore = theQueryResultStore;
238     }
239 
240     /***
241      * TODO: optimize!!
242      * 
243      * @see ca.uhn.cache.ISemanticCache#getEarliestCacheTime(ca.uhn.cache.IQuery)
244      */
245     public Date getEarliestCacheTime(IQuery theQuery) throws CacheException {
246         IQuery[] regions = QueryUtil.chunk(theQuery, myParamSpace);
247         
248         Date earliest = new Date(System.currentTimeMillis() + 20 * 60 * 1000);
249         for (int i = 0; i < regions.length; i++) {
250             IChunk chunk = myChunkStore.get(regions[i]);
251             if (chunk != null && chunk.getCacheTime().getTime() < earliest.getTime()) {
252                 earliest = chunk.getCacheTime();
253             }
254         }
255         
256         if (earliest.getTime() < System.currentTimeMillis()) {
257             return earliest;
258         } else {
259             return null; //this is a signal to skip the round trip to source 
260         }
261     }
262     
263 }