KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > riotfamily > cachius > spring > CacheableControllerHandlerAdapter


1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1
3  * The contents of this file are subject to the Mozilla Public License Version
4  * 1.1 (the "License"); you may not use this file except in compliance with
5  * the License. You may obtain a copy of the License at
6  * http://www.mozilla.org/MPL/
7  *
8  * Software distributed under the License is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
10  * for the specific language governing rights and limitations under the
11  * License.
12  *
13  * The Original Code is Riot.
14  *
15  * The Initial Developer of the Original Code is
16  * Neteye GmbH.
17  * Portions created by the Initial Developer are Copyright (C) 2006
18  * the Initial Developer. All Rights Reserved.
19  *
20  * Contributor(s):
21  * Felix Gnass [fgnass at neteye dot de]
22  *
23  * ***** END LICENSE BLOCK ***** */

24 package org.riotfamily.cachius.spring;
25
26 import javax.servlet.http.HttpServletRequest JavaDoc;
27 import javax.servlet.http.HttpServletResponse JavaDoc;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.riotfamily.cachius.Cache;
32 import org.riotfamily.cachius.CacheItem;
33 import org.riotfamily.cachius.CachiusResponseWrapper;
34 import org.riotfamily.cachius.ItemUpdater;
35 import org.riotfamily.cachius.support.SessionUtils;
36 import org.riotfamily.common.web.view.ViewResolverHelper;
37 import org.springframework.beans.factory.DisposableBean;
38 import org.springframework.context.ApplicationContext;
39 import org.springframework.context.ApplicationContextAware;
40 import org.springframework.core.Ordered;
41 import org.springframework.web.servlet.HandlerAdapter;
42 import org.springframework.web.servlet.ModelAndView;
43 import org.springframework.web.servlet.View;
44 import org.springframework.web.servlet.mvc.LastModified;
45
46 /**
47  * Adapter that handles {@link CacheableController cacheable controllers}.
48  * <p>
49  * The adapter checks if a Controller is cacheable and whether there is
50  * an up-to-date cache item which can be served. If not, the controller's
51  * <code>handleRequest()</code> method is invoked and the output is captured
52  * and written to the cache.
53  * </p>
54  * <p>
55  * Since 6.5 the adapter does no longer support regular controllers. In order
56  * to support both cacheable and non-cacheable controllers you hav to add
57  * a {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter}
58  * to your context manually.
59  * </p>
60  * @author Felix Gnass
61  */

62 public class CacheableControllerHandlerAdapter implements HandlerAdapter,
63         DisposableBean, ApplicationContextAware, Ordered {
64
65     private Log log = LogFactory.getLog(CacheableControllerHandlerAdapter.class);
66
67     private Cache cache;
68
69     private ViewResolverHelper viewResolverHelper;
70
71     private int order = 0;
72
73     private boolean enabled = true;
74
75     public CacheableControllerHandlerAdapter(Cache cache) {
76         this.cache = cache;
77     }
78
79     /**
80      * Sets whether this HandlerAdapter is enabled.
81      * Default is <code>true</code>. You can set this property to
82      * <code>false</code> in order to disable caching without removing the
83      * HandlerAdapter from the context.
84      */

85     public void setEnabled(boolean enabled) {
86         this.enabled = enabled;
87     }
88
89     /**
90      * Returns the order in which this HandlerAdapter is processed.
91      */

92     public int getOrder() {
93         return this.order;
94     }
95
96     /**
97      * Set the order in which this HandlerAdapter is processed.
98      */

99     public void setOrder(int order) {
100         this.order = order;
101     }
102
103     public void setApplicationContext(ApplicationContext context) {
104         viewResolverHelper = new ViewResolverHelper(context);
105     }
106
107     /**
108      * Persists the cache on shutdown.
109      * @see DisposableBean
110      */

111     public void destroy() {
112         cache.persist();
113     }
114
115     /**
116      * Returns <code>true</code> if handler implements the
117      * {@link CacheableController} interface.
118      */

119     public boolean supports(Object JavaDoc handler) {
120         return enabled && handler instanceof CacheableController;
121     }
122
123     /**
124      * Retrieves a CacheItem from the cache by calling
125      * {@link #getCacheItem(CacheableController, HttpServletRequest)}.
126      * If an item was found and it's up-to-date, the cached content is served.
127      * Otherwise {@link #handleRequestAndUpdateCacheItem} is invoked.
128      */

129     public ModelAndView handle(HttpServletRequest JavaDoc request,
130             HttpServletResponse JavaDoc response, Object JavaDoc handler) throws Exception JavaDoc {
131
132         CacheableController controller = (CacheableController) handler;
133         String JavaDoc cacheKey = controller.getCacheKey(request);
134         CacheItem cacheItem = getCacheItem(cacheKey, request);
135         if (cacheItem == null) {
136             log.debug("No cacheItem - Response won't be cached");
137             return controller.handleRequest(request, response);
138         }
139
140         if (isUpToDate(cacheItem, controller, request)) {
141             cacheItem.writeTo(request, response);
142             return null;
143         }
144         else {
145             return handleRequestAndUpdateCacheItem(request, response,
146                     controller, cacheItem);
147         }
148     }
149
150     protected ModelAndView handleRequestAndUpdateCacheItem(
151             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response,
152             CacheableController controller, CacheItem cacheItem)
153             throws Exception JavaDoc {
154
155         ItemUpdater update = new ItemUpdater(cacheItem, request);
156         CachiusResponseWrapper wrapper = new CachiusResponseWrapper(
157                 response, update);
158
159         ModelAndView mv = controller.handleRequest(request, wrapper);
160         if (mv == null) {
161             wrapper.flushBuffer();
162             update.updateCacheItem();
163             return null;
164         }
165         else {
166             View view = viewResolverHelper.resolveView(request, mv);
167             View cachingView = new CachingView(view, wrapper, update);
168             return new ModelAndView(cachingView, mv.getModel());
169         }
170     }
171
172     protected CacheItem getCacheItem(String JavaDoc cacheKey,
173             HttpServletRequest JavaDoc request) {
174
175         if (cacheKey == null) {
176             log.debug("Cache key is null - Response won't be cached.");
177             return null;
178         }
179
180         boolean encodedUrls = SessionUtils.urlsNeedEncoding(request);
181         if (encodedUrls) {
182             cacheKey += ";jsessionid";
183         }
184
185         log.debug("Getting cache item for key " + cacheKey);
186         CacheItem cacheItem = cache.getItem(cacheKey);
187         if (cacheItem == null) {
188             log.warn("Failed to create cache item");
189         }
190         else if (cacheItem.isNew() || !cacheItem.exists()) {
191             cacheItem.setFilterSessionId(encodedUrls);
192         }
193         return cacheItem;
194     }
195
196     /**
197      * Checks whether the given item is up-to-date. If the item is new or
198      * does not exist <code>false</code> is returned. Otherwise
199      * {@link CacheableController#getLastModified getLastModified()} is
200      * invoked on the controller and compared to the item's timestamp.
201      * <p>
202      * NOTE: As a side effect, the item's timestamp is set to the current
203      * time if a modification has occurred. This will prevent other threads
204      * from invoking handleRequest(), too.
205      * </p>
206      */

207     protected boolean isUpToDate(CacheItem cacheItem,
208             CacheableController controller, HttpServletRequest JavaDoc request)
209             throws Exception JavaDoc {
210
211         // No need to check if the item has just been constructed or
212
// the cache file has been deleted
213
if (cacheItem.isNew() || !cacheItem.exists()) {
214             log.debug("Item is new or has been invalidated");
215             return false;
216         }
217
218         long now = System.currentTimeMillis();
219         long ttl = controller.getTimeToLive();
220         if (ttl == CacheableController.CACHE_ETERNALLY) {
221             log.debug("Item is cached eternally");
222             return true;
223         }
224         if (cacheItem.getLastCheck() + ttl < now) {
225             long mtime = controller.getLastModified(request);
226             log.debug("Last modified: " + mtime);
227             cacheItem.setLastCheck(now);
228             if (mtime > cacheItem.getLastModified()) {
229                 // Update the timestamp so that other threads won't call
230
// the handleRequest() method, too. Note: For new items
231
// lastModified is set by the CacheItem.update() method.
232
cacheItem.setLastModified(now);
233                 return false;
234             }
235         }
236         else if (log.isDebugEnabled()) {
237             log.debug("Item not expired yet, will live for another " +
238                     (ttl - (now - cacheItem.getLastCheck())) + " ms.");
239         }
240         return true;
241     }
242
243     /**
244      * Returns the lastModified date as reported by the Controller/CacheItem.
245      */

246     public long getLastModified(HttpServletRequest JavaDoc request, Object JavaDoc handler) {
247         if (handler instanceof LastModified) {
248             CacheableController controller = (CacheableController) handler;
249             String JavaDoc cacheKey = controller.getCacheKey(request);
250             CacheItem cacheItem = getCacheItem(cacheKey, request);
251             if (cacheItem != null) {
252                 if (!cacheItem.isNew()) {
253                     long now = System.currentTimeMillis();
254                     long ttl = controller.getTimeToLive();
255                     if (ttl == CacheableController.CACHE_ETERNALLY
256                             || cacheItem.getLastCheck() + ttl <= now) {
257
258                         return cacheItem.getLastModified();
259                     }
260                 }
261                 try {
262                     return controller.getLastModified(request);
263                 }
264                 catch (Exception JavaDoc e) {
265                     log.error("Error invoking the last-modified method", e);
266                 }
267             }
268         }
269         return -1L;
270     }
271
272 }
Popular Tags