Source code: com/opencms/flex/cache/CmsFlexCacheEntry.java
1 /*
2 * File : $Source: /usr/local/cvs/opencms/src/com/opencms/flex/cache/Attic/CmsFlexCacheEntry.java,v $
3 * Date : $Date: 2003/05/13 13:18:20 $
4 * Version: $Revision: 1.9.2.1 $
5 *
6 * This library is part of OpenCms -
7 * the Open Source Content Mananagement System
8 *
9 * Copyright (C) 2002 - 2003 Alkacon Software (http://www.alkacon.com)
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * For further information about Alkacon Software, please see the
22 * company website: http://www.alkacon.com
23 *
24 * For further information about OpenCms, please see the
25 * project website: http://www.opencms.org
26 *
27 * You should have received a copy of the GNU Lesser General Public
28 * License along with this library; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 */
31
32 package com.opencms.flex.cache;
33
34 import com.opencms.flex.util.I_CmsFlexLruCacheObject;
35
36 import java.util.Iterator;
37 import java.util.Map;
38
39 /**
40 * Contains the contents of a cached resource.<p>
41 *
42 * It is basically a list of pre-generated output,
43 * include() calls to other resources (with request parameters) and http headers that this
44 * resource requires to be set.<p>
45 *
46 * A CmsFlexCacheEntry might also describe a redirect-call, but in this case
47 * nothing else will be cached.<p>
48 *
49 * The pre-generated output is saved in <code>byte[]</code> arrays.
50 * The include() calls are saved as Strings of the included resource name,
51 * the parameters for the calls are saved in a HashMap.
52 * The headers are saved in a HashMap.
53 * In case of a redirect, the redircet target is cached in a String.<p>
54 *
55 * The CmsFlexCacheEntry can also have a timeout value, which indicates the time
56 * that his entry will become invalid and should thus be cleared from the cache.<p>
57 *
58 * @author Alexander Kandzior (a.kandzior@alkacon.com)
59 * @author Thomas Weckert (t.weckert@alkacon.com)
60 * @see com.opencms.flex.util.I_CmsFlexLruCacheObject
61 * @version $Revision: 1.9.2.1 $
62 */
63 public class CmsFlexCacheEntry extends Object implements I_CmsFlexLruCacheObject {
64
65 /** Initial size for lists */
66 public static final int C_INITIAL_CAPACITY_LISTS = 11;
67 // Alternatives: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71
68
69 /** The list of items for this resource */
70 private java.util.List m_elements;
71
72 /** A Map of cached headers for this resource */
73 private java.util.Map m_headers;
74
75 /** A redirection target (if redirection is set) */
76 private String m_redirectTarget;
77
78 /** Debug switch */
79 private static final int DEBUG = 0;
80
81 /** Age for timeout */
82 private long m_timeout = -1;
83
84 /** Indicates if this cache entry is completed */
85 private boolean m_completed = false;
86
87 /** The CacheEntry's size in kBytes */
88 private int m_byteSize;
89
90 /** Pointer to the next cache entry in the LRU cache */
91 private I_CmsFlexLruCacheObject m_Next;
92
93 /** Pointer to the previous cache entry in the LRU cache. */
94 private I_CmsFlexLruCacheObject m_Previous;
95
96 /** The variation map where this cache entry is stored. */
97 private Map m_VariationMap;
98
99 /** The key under which this cache entry is stored in the variation map. */
100 private String m_VariationKey;
101
102 /** Static counter to give each entry a unique ID. */
103 private static int ID_COUNTER = 0;
104
105 /** The internal ID of this cache entry. */
106 private int ID;
107
108 /**
109 * Constructor for class CmsFlexCacheEntry.<p>
110 *
111 * The way to use this class is to first use this empty constructor
112 * and later add data with the various add methods.
113 */
114 public CmsFlexCacheEntry() {
115 m_elements = new java.util.ArrayList(C_INITIAL_CAPACITY_LISTS);
116 m_redirectTarget = null;
117 m_headers = null;
118 m_byteSize = 0;
119
120 this.setNextLruObject( null );
121 this.setPreviousLruObject( null );
122
123 this.ID = CmsFlexCacheEntry.ID_COUNTER++;
124 }
125
126 /**
127 * Adds an array of bytes to this cache entry,
128 * this will usually be the result of some kind of output - stream.<p>
129 *
130 * @param bytes the output to save in the cache
131 */
132 public void add(byte[] bytes) {
133 if (m_completed) return;
134 if (m_redirectTarget == null) {
135 // Add only if not already redirected
136 m_elements.add(bytes);
137 m_byteSize += bytes.length;
138 }
139 }
140
141 /**
142 * Add an include - call target resource to this cache entry.<p>
143 *
144 * @param resource a name of a resource in the OpenCms VFS
145 * @param parameters a map of parameters specific to this include call
146 */
147 public void add(String resource, java.util.Map paramters) {
148 if (m_completed) return;
149 if (m_redirectTarget == null) {
150 // Add only if not already redirected
151 m_elements.add(resource);
152 if (paramters == null) paramters = java.util.Collections.EMPTY_MAP;
153 m_elements.add(paramters);
154 m_byteSize += resource.getBytes().length;
155 }
156 }
157
158 /**
159 * Add a map of headers to this cache entry,
160 * which are usually collected in the class CmsFlexResponse first.<p>
161 *
162 * @param headers the map of headers to add to the entry
163 */
164 public void addHeaders(java.util.Map headers) {
165 if (m_completed) return;
166 m_headers = headers;
167
168 Iterator allHeaders = m_headers.keySet().iterator();
169 while (allHeaders.hasNext()) {
170 this.m_byteSize += ((String)allHeaders.next()).getBytes().length;
171 }
172 }
173
174 /**
175 * Set a redirect target for this cache entry.<p>
176 *
177 * <b>Important:</b>
178 * When a redirect target is set, all saved data is thrown away,
179 * and new data will not be saved in the cache entry.
180 * This is so since with a redirect nothing will be displayed
181 * in the browser anyway, so there is no point in saving the data.<p>
182 *
183 * @param target The redirect target (must be a valid URL).
184 */
185 public void setRedirect(String target) {
186 if (m_completed) return;
187 m_redirectTarget = target;
188 this.m_byteSize = target.getBytes().length;
189 // If we have a redirect we don't need any other output or headers
190 m_elements = null;
191 m_headers = null;
192 }
193
194 /**
195 * Returns the list of data entries of this cache entry.<p>
196 *
197 * Data entries are byte arrays representing some kind of ouput
198 * or Strings representing include calls to other resources.
199 *
200 * @return the list of data elements of this cache entry
201 */
202 public java.util.List elements() {
203 return m_elements;
204 }
205
206 /**
207 * Processing method for this cached entry.<p>
208 *
209 * If this method is called, it delivers the contents of
210 * the cached entry to the given request / response.
211 * This includes calls to all included resources.<p>
212 *
213 * @param req the request from the client
214 * @param res the server response
215 * @throws CmsException is thrown when problems writing to the response output-stream occur
216 * @throws ServletException might be thrown from call to RequestDispatcher.include()
217 * @throws IOException might be thrown from call to RequestDispatcher.include() or from Response.sendRedirect()
218 */
219 public void service(CmsFlexRequest req, CmsFlexResponse res)
220 throws com.opencms.core.CmsException, javax.servlet.ServletException, java.io.IOException {
221 if (!m_completed) return;
222
223 if (m_redirectTarget != null) {
224 res.setOnlyBuffering(false);
225 // Redirect the response, no further output required
226 res.sendRedirect(m_redirectTarget);
227 } else {
228 // Process cached headers first
229 CmsFlexResponse.processHeaders(m_headers, res);
230 // Check if this cache entry is a "leaf" (i.e. no further includes)
231 boolean hasNoSubElements = ((m_elements != null) && (m_elements.size() == 1));
232 // Write output to stream and process all included elements
233 java.util.Iterator i = m_elements.iterator();
234 while (i.hasNext()) {
235 Object o = i.next();
236 if (o instanceof String) {
237 // Handle cached parameters
238 java.util.Map map = (java.util.Map)i.next();
239 java.util.Map oldMap = null;
240 if (map.size() > 0) {
241 oldMap = req.getParameterMap();
242 req.addParameterMap(map);
243 }
244 // Do the include call
245 req.getRequestDispatcher((String)o).include(req, res);
246 // Reset parameters if neccessary
247 if (oldMap != null) req.setParameterMap(oldMap);
248 } else {
249 try {
250 res.writeToOutputStream((byte[])o, hasNoSubElements);
251 } catch (java.io.IOException e) {
252 String err = this.getClass().getName() + ": Could not write to response OutputStream. ";
253 if (DEBUG > 0) System.err.println(err);
254 throw new com.opencms.core.CmsException(err + "\n" + e, e);
255 }
256 }
257 }
258 }
259 }
260
261 /**
262 * Returns the timeout - value of this cache entry,
263 * this is set to the time when the entry becomes invalid.
264 *
265 * @return the timeout value for this resource
266 */
267 public long getTimeout() {
268 return m_timeout;
269 }
270
271 /**
272 * Sets a timeout value to this cache entry,
273 * which indicates the time this entry becomes invalid.<p>
274 *
275 * The timeout parameter represents the minute - intervall in which the cache entry
276 * is to be cleared.
277 * The intervall always starts at 0.00h.
278 * A value of 60 would indicate that this entry will reach it's timeout at the beginning of the next
279 * full hour, a timeout of 20 would indicate that the entry is invalidated at x.00, x.20 and x.40 of every hour etc.<p>
280 *
281 * @param timeout the timeout value to be set
282 */
283 public synchronized void setTimeout(long timeout) {
284 if (timeout < 0 || ! m_completed) return;
285
286 long now = System.currentTimeMillis();
287 long daytime = now % 86400000;
288 m_timeout = now - (daytime % timeout) + timeout;
289 if (DEBUG > 2) System.err.println("FlexCacheEntry: New entry timeout=" + m_timeout + " now=" + now + " remaining=" + (m_timeout - now) );
290 }
291
292 /**
293 * Completes this cache entry.<p>
294 *
295 * A completed cache entry is made "unmodifiable",
296 * so that no further data can be added and existing data can not be changed.
297 * This is to prevend the (unlikley) case that some user-written class
298 * tries to make changes to a cache entry.<p>
299 */
300 public void complete() {
301 m_completed = true;
302 // Prevent changing of the cached lists
303 if (m_headers != null) {
304 m_headers = java.util.Collections.unmodifiableMap(m_headers);
305 }
306 if (m_elements != null) {
307 m_elements = java.util.Collections.unmodifiableList(m_elements);
308 }
309 if (DEBUG > 1) System.err.println("CmsFlexCacheEntry: New entry completed:\n" + this.toString());
310 }
311
312 /**
313 * @see java.lang.Object#toString()
314 *
315 * @return a basic String representation of this CmsFlexCache entry
316 */
317 public String toString() {
318 String str = null;
319 if (m_redirectTarget == null) {
320 str = "CmsFlexCacheEntry [" + m_elements.size() + " Elements/" + this.getLruCacheCosts() + " bytes]\n";
321 java.util.Iterator i = m_elements.iterator();
322 int count = 0;
323 while (i.hasNext()) {
324 count++;
325 Object o = i.next();
326 if (o instanceof String) {
327 str += "" + count + " - <cms:include target=" + o + ">\n";
328 } else {
329 str += "" + count + " - <![CDATA[" + new String((byte[])o) + "]]>\n";
330 }
331 }
332 } else {
333 str = "CmsFlexCacheEntry [Redirect to target=" + m_redirectTarget + "]";
334 }
335 return str;
336 }
337
338 /**
339 * Stores a backward reference to the map and key where this cache entry is stored.
340 *
341 * This is required for the FlexCache.<p>
342 *
343 * @param theVariationKey the variation key
344 * @param theVariationMap the variation map
345 */
346 public void setVariationData( String theVariationKey, Map theVariationMap ) {
347 this.m_VariationKey = theVariationKey;
348 this.m_VariationMap = theVariationMap;
349 }
350
351 // implementation of the com.opencms.flex.util.I_CmsFlexLruCacheObject interface methods
352
353 /**
354 * @see com.opencms.flex.util.I_CmsFlexLruCacheObject#setNextLruObject(com.opencms.flex.util.I_CmsFlexLruCacheObject)
355 */
356 public void setNextLruObject( I_CmsFlexLruCacheObject theNextEntry ) {
357 this.m_Next = theNextEntry;
358 }
359
360 /**
361 * @see com.opencms.flex.util.I_CmsFlexLruCacheObject#getNextLruObject()
362 */
363 public I_CmsFlexLruCacheObject getNextLruObject() {
364 return this.m_Next;
365 }
366
367 /**
368 * @see com.opencms.flex.util.I_CmsFlexLruCacheObject#setPreviousLruObject(com.opencms.flex.util.I_CmsFlexLruCacheObject)
369 */
370 public void setPreviousLruObject( I_CmsFlexLruCacheObject thePreviousEntry ) {
371 this.m_Previous = thePreviousEntry;
372 }
373
374 /**
375 * @see com.opencms.flex.util.I_CmsFlexLruCacheObject#getPreviousLruObject()
376 */
377 public I_CmsFlexLruCacheObject getPreviousLruObject() {
378 return this.m_Previous;
379 }
380
381 /**
382 * @see com.opencms.flex.util.I_CmsFlexLruCacheObject#addToLruCache()
383 */
384 public void addToLruCache() {
385 // do nothing here...
386 if (DEBUG>0) System.out.println( "Added cache entry with ID: " + this.ID + " to the LRU cache" );
387 }
388
389 /**
390 * @see com.opencms.flex.util.I_CmsFlexLruCacheObject#removeFromLruCache()
391 */
392 public void removeFromLruCache() {
393 if (m_VariationMap!=null && this.m_VariationKey!=null) {
394 this.m_VariationMap.remove( this.m_VariationKey );
395 if (DEBUG>0) System.err.println( "Removed cache entry with ID: " + this.ID + " from the LRU cache" );
396 }
397 }
398
399 /**
400 * @see com.opencms.flex.util.I_CmsFlexLruCacheObject#getLruCacheCosts()
401 */
402 public int getLruCacheCosts() {
403 return m_byteSize;
404 }
405
406 // methods to clean-up/finalize the object instance
407
408 /**
409 * Finalize this instance.<p>
410 *
411 * @see java.lang.Object#finalize()
412 */
413 protected void finalize() throws java.lang.Throwable {
414 if (DEBUG>0) System.err.println( "Finalizing cache entry with ID: " + this.ID );
415
416 this.clear();
417
418 this.m_elements = null;
419 this.m_headers = null;
420
421 this.m_VariationKey = null;
422 this.m_VariationMap = null;
423
424 this.setNextLruObject( null );
425 this.setPreviousLruObject( null );
426
427 this.m_byteSize = 0;
428
429 super.finalize();
430 }
431
432 /**
433 * Clears the elements and headers HashMaps.<p>
434 */
435 private void clear() {
436 m_elements.clear();
437 m_headers.clear();
438 }
439 }