KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > eviction > ExpirationAlgorithm


1 package org.jboss.cache.eviction;
2
3 import org.apache.commons.logging.Log;
4 import org.apache.commons.logging.LogFactory;
5 import org.jboss.cache.Fqn;
6 import org.jboss.cache.FqnComparator;
7 import org.jboss.cache.Region;
8
9 import java.util.Iterator JavaDoc;
10 import java.util.SortedSet JavaDoc;
11 import java.util.TreeSet JavaDoc;
12
13 /**
14  * Eviction algorithm that uses a key in the Node data that indicates the time
15  * the node should be evicted. The key must be a java.lang.Long object, with
16  * the time to expire as milliseconds past midnight January 1st, 1970 UTC (the
17  * same relative time as provided by {@link
18  * java.lang.System#currentTimeMillis()}).
19  * <p/>
20  * <!--
21  * Alternatively to setting the Node expiration key value, calling {@link
22  * Region#markNodeCurrentlyInUse} will cause the node to expire once the
23  * timeout is reached. Note that, because the expiration value is not stored on
24  * the node, the expiration time cannot be persisted or replicated. Calling
25  * {@link Region#unmarkNodeCurrentlyInUse} unsets the expiration value.
26  * <p/>
27  * -->
28  * This algorithm also obeys the configuration key {@link
29  * ExpirationConfiguration#getMaxNodes()}, and will evict the soonest to
30  * expire entires first to reduce the region size. If there are not enough
31  * nodes with expiration keys set, a warning is logged.
32  * <p/>
33  * If a node in the eviction region does not have an expiration value, then
34  * {@link ExpirationConfiguration#getTimeToLiveSeconds} (if set) will be used.
35  * The expiration is updated when a node is added or updated.
36  * <p/>
37  * If there is no time-to-live set, and a node in the eviction region does not
38  * have an expiration value, then that node will never be evicted. As
39  * forgetting to indicate an expiration value is likely a mistake, a warning
40  * message is logged by this class. This warning, however, can be disabled
41  * through {@link ExpirationConfiguration#setWarnNoExpirationKey(boolean)}/
42  * <p/>
43  * A node's expiration time can be changed by setting a new value in the node.
44  * <p/>
45  * Example usage:
46  * <pre>
47  * Cache cache;
48  * Fqn fqn1 = Fqn.fromString("/node/1");
49  * Long future = new Long(System.currentTimeMillis() + 2000);
50  * cache.put(fqn1, ExpirationConfiguration.EXPIRATION_KEY, future);
51  * cache.put(fqn1, "foo");
52  * assertTrue(cache.get(fqn1) != null);
53  * <p/>
54  * Thread.sleep(5000); // 5 seconds
55  * assertTrue(cache.get(fqn1) == null);
56  * <p/>
57  * <!--
58  * // Alternatively, this expires a node in one second
59  * cache.put(fqn1, "foo");
60  * cache.getRegion(fqn1, false).markNodeCurrentlyInUse(fqn1, 1000);
61  * Thread.sleep(2000); // 5 seconds
62  * assertTrue(cache.get(fqn1) == null);
63  * -->
64  * </pre>
65  */

66 public class ExpirationAlgorithm extends BaseEvictionAlgorithm
67 {
68
69    private static final Log log = LogFactory.getLog(ExpirationAlgorithm.class);
70
71    private ExpirationConfiguration config;
72
73    private ExpirationPolicy policy;
74
75    private SortedSet JavaDoc<ExpirationEntry> set;
76
77    /**
78     * Constructs a new algorithm with a policy.
79     */

80    public ExpirationAlgorithm(ExpirationPolicy policy)
81    {
82       this.policy = policy;
83       this.set = new TreeSet JavaDoc<ExpirationEntry>();
84    }
85
86    private void addEvictionEntry(EvictedEventNode node)
87    {
88       Fqn fqn = node.getFqn();
89       addEvictionEntry(fqn);
90    }
91
92    private void addEvictionEntry(Fqn fqn)
93    {
94       Long JavaDoc l = getExpiration(fqn);
95       if (l == null)
96       {
97          if (config.getWarnNoExpirationKey())
98             log.warn("No expiration key '" + config.getExpirationKeyName() + "' for Node: " + fqn);
99          else if (log.isDebugEnabled())
100             log.debug("No expiration key for Node: " + fqn);
101       }
102       else
103       {
104          setExpiration(fqn, l);
105       }
106    }
107
108    private void setExpiration(Fqn fqn, Long JavaDoc l)
109    {
110       boolean found = set.remove(new ExpirationEntry(fqn));
111       if (found && log.isTraceEnabled())
112          log.trace("removed old expiration for " + fqn);
113       ExpirationEntry ee = new ExpirationEntry(fqn, l);
114       if (log.isTraceEnabled())
115          log.trace("adding eviction entry: " + ee);
116       set.add(ee);
117    }
118
119    private Long JavaDoc getExpiration(Fqn fqn)
120    {
121       Long JavaDoc l = (Long JavaDoc) policy.getCacheData(fqn, config.getExpirationKeyName());
122       if (l == null)
123          return null;
124       return l;
125    }
126
127    @Override JavaDoc
128    protected void processQueues(Region region) throws EvictionException
129    {
130       EvictedEventNode node;
131       int count = 0;
132       while ((node = region.takeLastEventNode()) != null)
133       {
134          count++;
135          switch (node.getEventType())
136          {
137             case ADD_NODE_EVENT:
138             case ADD_ELEMENT_EVENT:
139                addEvictionEntry(node);
140                break;
141             case REMOVE_ELEMENT_EVENT:
142             case REMOVE_NODE_EVENT:
143             case UNMARK_USE_EVENT:
144                removeEvictionEntry(node);
145                break;
146             case VISIT_NODE_EVENT:
147                // unused
148
break;
149             case MARK_IN_USE_EVENT:
150                markInUse(node);
151                break;
152             default:
153                throw new RuntimeException JavaDoc("Illegal Eviction Event type " + node.getEventType());
154          }
155       }
156
157       if (log.isTraceEnabled())
158       {
159          log.trace("processed " + count + " node events in region: " + region.getFqn());
160       }
161    }
162
163    private void markInUse(EvictedEventNode node)
164    {
165       long expiration = node.getInUseTimeout() + System.currentTimeMillis();
166       setExpiration(node.getFqn(), expiration);
167    }
168
169    @Override JavaDoc
170    protected void prune() throws EvictionException
171    {
172       if (set.isEmpty())
173          return;
174       long now = System.currentTimeMillis();
175       int max = config.getMaxNodes();
176       for (Iterator JavaDoc<ExpirationEntry> i = set.iterator(); i.hasNext();)
177       {
178          ExpirationEntry ee = i.next();
179          if (ee.getExpiration() < now || (max != 0 && set.size() > max))
180          {
181             i.remove();
182             evictCacheNode(ee.getFqn());
183          }
184          else
185          {
186             break;
187          }
188       }
189       if (max != 0 && max > set.size())
190          log.warn("Unable to remove nodes to reduce region size below " +
191                  config.getMaxNodes() + ". " +
192                  "Set expiration for nodes in this region");
193    }
194
195    private void removeEvictionEntry(EvictedEventNode node)
196    {
197       Fqn fqn = node.getFqn();
198       if (getExpiration(fqn) == null)
199          set.remove(new ExpirationEntry(fqn));
200    }
201
202    @Override JavaDoc
203    public void resetEvictionQueue(Region region)
204    {
205       for (ExpirationEntry ee : set)
206       {
207          addEvictionEntry(ee.getFqn());
208       }
209    }
210
211    @Override JavaDoc
212    protected EvictionQueue setupEvictionQueue(Region region) throws EvictionException
213    {
214       this.region = region;
215       this.config = (ExpirationConfiguration) region.getEvictionPolicyConfig();
216       return new DummyEvictionQueue();
217    }
218
219    @Override JavaDoc
220    protected boolean shouldEvictNode(NodeEntry ne)
221    {
222       throw new UnsupportedOperationException JavaDoc();
223    }
224
225    /**
226     * Ordered list of FQN, with the expiration taken from the Map at the time
227     * of processing.
228     */

229    static class ExpirationEntry implements Comparable JavaDoc<ExpirationEntry>
230    {
231
232       private long expiration;
233
234       private Fqn fqn;
235
236       public ExpirationEntry(Fqn fqn)
237       {
238          this.fqn = fqn;
239       }
240
241       public ExpirationEntry(Fqn fqn, long expiration)
242       {
243          this.fqn = fqn;
244          this.expiration = expiration;
245       }
246
247       /**
248        * Returns true if the FQN are the same, else compares expiration, then FQN order.
249        */

250       public int compareTo(ExpirationEntry ee)
251       {
252          if (fqn.equals(ee.fqn))
253             return 0;
254          long n = expiration - ee.expiration;
255          if (n < 0)
256             return -1;
257          if (n > 0)
258             return 1;
259          return FqnComparator.INSTANCE.compare(fqn, ee.fqn);
260       }
261
262       /**
263        * @return the expiration
264        */

265       public long getExpiration()
266       {
267          return expiration;
268       }
269
270       /**
271        * @return the fqn
272        */

273       public Fqn getFqn()
274       {
275          return fqn;
276       }
277
278       public boolean equals(Object JavaDoc o)
279       {
280          if (!(o instanceof ExpirationEntry))
281             return false;
282          ExpirationEntry ee = (ExpirationEntry) o;
283          return fqn.equals(ee.fqn);
284       }
285
286       public int hashCode()
287       {
288          return fqn.hashCode();
289       }
290
291       public String JavaDoc toString()
292       {
293          long now = System.currentTimeMillis();
294          long ttl = expiration - now;
295          String JavaDoc sttl;
296          if (ttl > 1000 * 60)
297             sttl = (ttl / (1000 * 60)) + "min";
298          else if (ttl > 1000)
299             sttl = (ttl / 1000) + "s";
300          else
301             sttl = ttl + "ms";
302          return "EE fqn=" + fqn + " ttl=" + sttl;
303       }
304    }
305
306    class DummyEvictionQueue implements EvictionQueue
307    {
308
309       public void addNodeEntry(NodeEntry entry)
310       {
311          throw new UnsupportedOperationException JavaDoc();
312       }
313
314       public void clear()
315       {
316          set.clear();
317       }
318
319       public boolean containsNodeEntry(NodeEntry entry)
320       {
321          return false;
322       }
323
324       public NodeEntry getFirstNodeEntry()
325       {
326          return null;
327       }
328
329       public NodeEntry getNodeEntry(Fqn fqn)
330       {
331          return null;
332       }
333
334       public NodeEntry getNodeEntry(String JavaDoc fqn)
335       {
336          return null;
337       }
338
339       public int getNumberOfElements()
340       {
341          return set.size();
342       }
343
344       public int getNumberOfNodes()
345       {
346          return set.size();
347       }
348
349       public Iterator JavaDoc iterate()
350       {
351          return null;
352       }
353
354       public void modifyElementCount(int difference)
355       {
356       }
357
358       public void removeNodeEntry(NodeEntry entry)
359       {
360          throw new UnsupportedOperationException JavaDoc();
361       }
362
363    }
364
365 }
366
Popular Tags