001: /*
002: * Copyright (c) 2002-2003 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.oscache.base;
006:
007: import com.opensymphony.oscache.web.filter.ResponseContent;
008:
009: import java.io.Serializable;
010:
011: import java.util.Collection;
012: import java.util.HashSet;
013: import java.util.Set;
014:
015: /**
016: * A CacheEntry instance represents one entry in the cache. It holds the object that
017: * is being cached, along with a host of information about that entry such as the
018: * cache key, the time it was cached, whether the entry has been flushed or not and
019: * the groups it belongs to.
020: *
021: * @version $Revision: 388 $
022: * @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
023: * @author <a href="mailto:tgochenour@peregrine.com">Todd Gochenour</a>
024: * @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
025: */
026: public class CacheEntry implements Serializable {
027: /**
028: * Default initialization value for the creation time and the last
029: * update time. This is a placeholder that indicates the value has
030: * not been set yet.
031: */
032: private static final byte NOT_YET = -1;
033:
034: /**
035: * Specifying this as the refresh period for the
036: * {@link #needsRefresh(int)} method will ensure
037: * an entry does not become stale until it is
038: * either explicitly flushed or a custom refresh
039: * policy causes the entry to expire.
040: */
041: public static final int INDEFINITE_EXPIRY = -1;
042:
043: /**
044: * The entry refresh policy object to use for this cache entry. This is optional.
045: */
046: private EntryRefreshPolicy policy = null;
047:
048: /**
049: * The actual content that is being cached. Wherever possible this object
050: * should be serializable. This allows <code>PersistenceListener</code>s
051: * to serialize the cache entries to disk or database.
052: */
053: private Object content = null;
054:
055: /**
056: * The set of cache groups that this cache entry belongs to, if any.
057: */
058: private Set groups = null;
059:
060: /**
061: * The unique cache key for this entry
062: */
063: private String key;
064:
065: /**
066: * <code>true</code> if this entry was flushed
067: */
068: private boolean wasFlushed = false;
069:
070: /**
071: * The time this entry was created.
072: */
073: private long created = NOT_YET;
074:
075: /**
076: * The time this emtry was last updated.
077: */
078: private long lastUpdate = NOT_YET;
079:
080: /**
081: * Construct a new CacheEntry using the key provided.
082: *
083: * @param key The key of this CacheEntry
084: */
085: public CacheEntry(String key) {
086: this (key, null);
087: }
088:
089: /**
090: * Construct a CacheEntry.
091: *
092: * @param key The unique key for this <code>CacheEntry</code>.
093: * @param policy Object that implements refresh policy logic. This parameter
094: * is optional.
095: */
096: public CacheEntry(String key, EntryRefreshPolicy policy) {
097: this (key, policy, null);
098: }
099:
100: /**
101: * Construct a CacheEntry.
102: *
103: * @param key The unique key for this <code>CacheEntry</code>.
104: * @param policy The object that implements the refresh policy logic. This
105: * parameter is optional.
106: * @param groups The groups that this <code>CacheEntry</code> belongs to. This
107: * parameter is optional.
108: */
109: public CacheEntry(String key, EntryRefreshPolicy policy,
110: String[] groups) {
111: this .key = key;
112:
113: if (groups != null) {
114: this .groups = new HashSet(groups.length);
115:
116: for (int i = 0; i < groups.length; i++) {
117: this .groups.add(groups[i]);
118: }
119: }
120:
121: this .policy = policy;
122: this .created = System.currentTimeMillis();
123: }
124:
125: /**
126: * Sets the actual content that is being cached. Wherever possible this
127: * object should be <code>Serializable</code>, however it is not an
128: * absolute requirement when using a memory-only cache. Being <code>Serializable</code>
129: * allows <code>PersistenceListener</code>s to serialize the cache entries to disk
130: * or database.
131: *
132: * @param value The content to store in this CacheEntry.
133: */
134: public synchronized void setContent(Object value) {
135: content = value;
136: lastUpdate = System.currentTimeMillis();
137: wasFlushed = false;
138: }
139:
140: /**
141: * Get the cached content from this CacheEntry.
142: *
143: * @return The content of this CacheEntry.
144: */
145: public Object getContent() {
146: return content;
147: }
148:
149: /**
150: * Get the date this CacheEntry was created.
151: *
152: * @return The date this CacheEntry was created.
153: */
154: public long getCreated() {
155: return created;
156: }
157:
158: /**
159: * Sets the cache groups for this entry.
160: *
161: * @param groups A string array containing all the group names
162: */
163: public synchronized void setGroups(String[] groups) {
164: if (groups != null) {
165: this .groups = new HashSet(groups.length);
166:
167: for (int i = 0; i < groups.length; i++) {
168: this .groups.add(groups[i]);
169: }
170: } else {
171: this .groups = null;
172: }
173:
174: lastUpdate = System.currentTimeMillis();
175: }
176:
177: /**
178: * Sets the cache groups for this entry
179: *
180: * @param groups A collection containing all the group names
181: */
182: public synchronized void setGroups(Collection groups) {
183: if (groups != null) {
184: this .groups = new HashSet(groups);
185: } else {
186: this .groups = null;
187: }
188:
189: lastUpdate = System.currentTimeMillis();
190: }
191:
192: /**
193: * Gets the cache groups that this cache entry belongs to.
194: * These returned groups should be treated as immuatable.
195: *
196: * @return A set containing the names of all the groups that
197: * this cache entry belongs to.
198: */
199: public Set getGroups() {
200: return groups;
201: }
202:
203: /**
204: * Get the key of this CacheEntry
205: *
206: * @return The key of this CacheEntry
207: */
208: public String getKey() {
209: return key;
210: }
211:
212: /**
213: * Set the date this CacheEntry was last updated.
214: *
215: * @param update The time (in milliseconds) this CacheEntry was last updated.
216: */
217: public void setLastUpdate(long update) {
218: lastUpdate = update;
219: }
220:
221: /**
222: * Get the date this CacheEntry was last updated.
223: *
224: * @return The date this CacheEntry was last updated.
225: */
226: public long getLastUpdate() {
227: return lastUpdate;
228: }
229:
230: /**
231: * Indicates whether this CacheEntry is a freshly created one and
232: * has not yet been assigned content or placed in a cache.
233: *
234: * @return <code>true</code> if this entry is newly created
235: */
236: public boolean isNew() {
237: return lastUpdate == NOT_YET;
238: }
239:
240: /**
241: * Get the size of the cache entry in bytes (roughly).<p>
242: *
243: * Currently this method only handles <code>String<code>s and
244: * {@link ResponseContent} objects.
245: *
246: * @return The approximate size of the entry in bytes, or -1 if the
247: * size could not be estimated.
248: */
249: public int getSize() {
250: // a char is two bytes
251: int size = (key.length() * 2) + 4;
252:
253: if (content.getClass() == String.class) {
254: size += ((content.toString().length() * 2) + 4);
255: } else if (content instanceof ResponseContent) {
256: size += ((ResponseContent) content).getSize();
257: } else {
258: return -1;
259: }
260:
261: //add created, lastUpdate, and wasFlushed field sizes (1, 8, and 8)
262: return size + 17;
263: }
264:
265: /**
266: * Flush the entry from cache.
267: * note that flushing the cache doesn't actually remove the cache contents
268: * it just tells the CacheEntry that it needs a refresh next time it is asked
269: * this is so that the content is still there for a <usecached />.
270: */
271: public void flush() {
272: wasFlushed = true;
273: }
274:
275: /**
276: * Check if this CacheEntry needs to be refreshed.
277: *
278: * @param refreshPeriod The period of refresh (in seconds). Passing in
279: * {@link #INDEFINITE_EXPIRY} will result in the content never becoming
280: * stale unless it is explicitly flushed, or expired by a custom
281: * {@link EntryRefreshPolicy}. Passing in 0 will always result in a
282: * refresh being required.
283: *
284: * @return Whether or not this CacheEntry needs refreshing.
285: */
286: public boolean needsRefresh(int refreshPeriod) {
287: boolean needsRefresh;
288:
289: // needs a refresh if it has never been updated
290: if (lastUpdate == NOT_YET) {
291: needsRefresh = true;
292: }
293: // Was it flushed from cache?
294: else if (wasFlushed) {
295: needsRefresh = true;
296: } else if (refreshPeriod == 0) {
297: needsRefresh = true;
298: }
299: // check what the policy has to say if there is one
300: else if (policy != null) {
301: needsRefresh = policy.needsRefresh(this );
302: }
303: // check if the last update + update period is in the past
304: else if ((refreshPeriod >= 0)
305: && (System.currentTimeMillis() >= (lastUpdate + (refreshPeriod * 1000L)))) {
306: needsRefresh = true;
307: } else {
308: needsRefresh = false;
309: }
310:
311: return needsRefresh;
312: }
313: }
|