Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 347   Methods: 30
NCLOC: 245   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
ExpirationAlgorithm.java 58.8% 54% 43.3% 53%
coverage coverage
 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.NodeSPI;
 7    import org.jboss.cache.Region;
 8   
 9    import java.util.Iterator;
 10    import java.util.SortedSet;
 11    import java.util.TreeSet;
 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    * This algorithm also obeys the configuration key {@link
 21    * ExpirationConfiguration#getMaxNodes()}, and will evict the soonest to
 22    * expire entires first to reduce the region size. If there are not enough
 23    * nodes with expiration keys set, a warning is logged.
 24    * <p/>
 25    * If a node in the eviction region does not have an expiration value, then
 26    * {@link ExpirationConfiguration#getTimeToLiveSeconds} (if set) will be used.
 27    * The expiration is updated when a node is added or updated.
 28    * <p/>
 29    * If there is no time-to-live set, and a node in the eviction region does not
 30    * have an expiration value, then that node will never be evicted. As
 31    * forgetting to indicate an expiration value is likely a mistake, a warning
 32    * message is logged by this class. This warning, however, can be disabled
 33    * through {@link ExpirationConfiguration#setWarnNoExpirationKey(boolean)}.
 34    * <p/>
 35    * A node's expiration time can be changed by setting a new value in the node.
 36    * <p/>
 37    * Example usage:
 38    * <pre>
 39    * Cache cache;
 40    * Fqn fqn1 = Fqn.fromString("/node/1");
 41    * Long future = new Long(System.currentTimeMillis() + 2000);
 42    * cache.put(fqn1, ExpirationConfiguration.EXPIRATION_KEY, future);
 43    * cache.put(fqn1, "foo");
 44    * assertTrue(cache.get(fqn1) != null);
 45    * <p/>
 46    * Thread.sleep(5000); // 5 seconds
 47    * assertTrue(cache.get(fqn1) == null);
 48    * <p/>
 49    * </pre>
 50    */
 51    public class ExpirationAlgorithm extends BaseEvictionAlgorithm
 52    {
 53   
 54    private static final Log log = LogFactory.getLog(ExpirationAlgorithm.class);
 55   
 56    private ExpirationConfiguration config;
 57   
 58    private ExpirationPolicy policy;
 59   
 60    private SortedSet<ExpirationEntry> set;
 61   
 62    /**
 63    * Constructs a new algorithm with a policy.
 64    */
 65  8 public ExpirationAlgorithm(ExpirationPolicy policy)
 66    {
 67  8 this.policy = policy;
 68  8 this.set = new TreeSet<ExpirationEntry>();
 69    }
 70   
 71  12 private void addEvictionEntry(EvictedEventNode node)
 72    {
 73  12 Fqn fqn = node.getFqn();
 74  12 addEvictionEntry(fqn);
 75    }
 76   
 77  12 private void addEvictionEntry(Fqn fqn)
 78    {
 79  12 Long l = getExpiration(fqn);
 80  12 if (l == null)
 81    {
 82  1 if (config.getWarnNoExpirationKey())
 83  1 log.warn("No expiration key '" + config.getExpirationKeyName() + "' for Node: " + fqn);
 84  0 else if (log.isDebugEnabled())
 85  0 log.debug("No expiration key for Node: " + fqn);
 86    }
 87    else
 88    {
 89  11 setExpiration(fqn, l);
 90    }
 91    }
 92   
 93  11 private void setExpiration(Fqn fqn, Long l)
 94    {
 95  11 ExpirationEntry ee = new ExpirationEntry(fqn, l);
 96  11 if (log.isTraceEnabled())
 97  0 log.trace("adding eviction entry: " + ee);
 98  11 set.add(ee);
 99    }
 100   
 101  32 @SuppressWarnings("unchecked")
 102    private Long getExpiration(Fqn fqn)
 103    {
 104  32 NodeSPI<String, Long> n = policy.getCache().peek(fqn, false);
 105  32 if (n == null)
 106  1 return null;
 107  31 return n.getDirect(config.getExpirationKeyName());
 108    }
 109   
 110  16 @Override
 111    protected void processQueues(Region region) throws EvictionException
 112    {
 113  16 EvictedEventNode node;
 114  16 int count = 0;
 115  ? while ((node = region.takeLastEventNode()) != null)
 116    {
 117  12 count++;
 118  12 switch (node.getEventType())
 119    {
 120  0 case ADD_NODE_EVENT:
 121  12 case ADD_ELEMENT_EVENT:
 122  12 addEvictionEntry(node);
 123  12 break;
 124  0 case REMOVE_ELEMENT_EVENT:
 125  0 case REMOVE_NODE_EVENT:
 126  0 case UNMARK_USE_EVENT:
 127    // Removals will be noticed when double-checking expiry time
 128    // removeEvictionEntry(node);
 129  0 break;
 130  0 case VISIT_NODE_EVENT:
 131    // unused
 132  0 break;
 133  0 case MARK_IN_USE_EVENT:
 134  0 markInUse(node);
 135  0 break;
 136  0 default:
 137  0 throw new RuntimeException("Illegal Eviction Event type " + node.getEventType());
 138    }
 139    }
 140   
 141  16 if (log.isTraceEnabled())
 142    {
 143  0 log.trace("processed " + count + " node events in region: " + region.getFqn());
 144    }
 145    }
 146   
 147  0 private void markInUse(EvictedEventNode node)
 148    {
 149  0 long expiration = node.getInUseTimeout() + System.currentTimeMillis();
 150  0 setExpiration(node.getFqn(), expiration);
 151    }
 152   
 153  16 @Override
 154    protected void prune() throws EvictionException
 155    {
 156  16 if (set.isEmpty())
 157  2 return;
 158  14 long now = System.currentTimeMillis();
 159  14 int max = config.getMaxNodes();
 160  14 for (Iterator<ExpirationEntry> i = set.iterator(); i.hasNext();)
 161    {
 162  20 ExpirationEntry ee = i.next();
 163  20 Long ce = getExpiration(ee.getFqn());
 164  20 if (ce == null || ce > ee.getExpiration())
 165    {
 166    // Expiration now older
 167  2 i.remove();
 168  2 continue;
 169    }
 170  18 if (ee.getExpiration() < now || (max != 0 && set.size() > max))
 171    {
 172  7 i.remove();
 173  7 evictCacheNode(ee.getFqn());
 174    }
 175    else
 176    {
 177  11 break;
 178    }
 179    }
 180  14 if (max != 0 && max > set.size())
 181  0 log.warn("Unable to remove nodes to reduce region size below " +
 182    config.getMaxNodes() + ". " +
 183    "Set expiration for nodes in this region");
 184    }
 185   
 186  0 @Override
 187    public void resetEvictionQueue(Region region)
 188    {
 189  0 for (ExpirationEntry ee : set)
 190    {
 191  0 addEvictionEntry(ee.getFqn());
 192    }
 193    }
 194   
 195  4 @Override
 196    protected EvictionQueue setupEvictionQueue(Region region) throws EvictionException
 197    {
 198  4 this.region = region;
 199  4 this.config = (ExpirationConfiguration) region.getEvictionPolicyConfig();
 200  4 return new DummyEvictionQueue();
 201    }
 202   
 203  0 @Override
 204    protected boolean shouldEvictNode(NodeEntry ne)
 205    {
 206  0 throw new UnsupportedOperationException();
 207    }
 208   
 209    /**
 210    * Ordered list of FQN, with the expiration taken from the Map at the time
 211    * of processing.
 212    */
 213    static class ExpirationEntry implements Comparable<ExpirationEntry>
 214    {
 215   
 216    private long expiration;
 217   
 218    private Fqn fqn;
 219   
 220  0 public ExpirationEntry(Fqn fqn)
 221    {
 222  0 this.fqn = fqn;
 223    }
 224   
 225  11 public ExpirationEntry(Fqn fqn, long expiration)
 226    {
 227  11 this.fqn = fqn;
 228  11 this.expiration = expiration;
 229    }
 230   
 231    /**
 232    * Compares expiration, then FQN order.
 233    */
 234  8 public int compareTo(ExpirationEntry ee)
 235    {
 236  8 long n = expiration - ee.expiration;
 237  8 if (n < 0)
 238  5 return -1;
 239  3 if (n > 0)
 240  1 return 1;
 241  2 return fqn.compareTo(ee.fqn);
 242    }
 243   
 244    /**
 245    * @return the expiration
 246    */
 247  37 public long getExpiration()
 248    {
 249  37 return expiration;
 250    }
 251   
 252    /**
 253    * @return the fqn
 254    */
 255  27 public Fqn getFqn()
 256    {
 257  27 return fqn;
 258    }
 259   
 260  0 public boolean equals(Object o)
 261    {
 262  0 if (!(o instanceof ExpirationEntry))
 263  0 return false;
 264  0 ExpirationEntry ee = (ExpirationEntry) o;
 265  0 return expiration == ee.expiration && fqn.equals(ee.fqn);
 266    }
 267   
 268  0 public int hashCode()
 269    {
 270  0 return (int)expiration ^ fqn.hashCode();
 271    }
 272   
 273  0 public String toString()
 274    {
 275  0 long now = System.currentTimeMillis();
 276  0 long ttl = expiration - now;
 277  0 String sttl;
 278  0 if (ttl > 1000 * 60)
 279  0 sttl = (ttl / (1000 * 60)) + "min";
 280  0 else if (ttl > 1000)
 281  0 sttl = (ttl / 1000) + "s";
 282    else
 283  0 sttl = ttl + "ms";
 284  0 return "EE fqn=" + fqn + " ttl=" + sttl;
 285    }
 286    }
 287   
 288    class DummyEvictionQueue implements EvictionQueue
 289    {
 290   
 291  0 public void addNodeEntry(NodeEntry entry)
 292    {
 293  0 throw new UnsupportedOperationException();
 294    }
 295   
 296  0 public void clear()
 297    {
 298  0 set.clear();
 299    }
 300   
 301  0 public boolean containsNodeEntry(NodeEntry entry)
 302    {
 303  0 return false;
 304    }
 305   
 306  0 public NodeEntry getFirstNodeEntry()
 307    {
 308  0 return null;
 309    }
 310   
 311  0 public NodeEntry getNodeEntry(Fqn fqn)
 312    {
 313  0 return null;
 314    }
 315   
 316  0 public NodeEntry getNodeEntry(String fqn)
 317    {
 318  0 return null;
 319    }
 320   
 321  0 public int getNumberOfElements()
 322    {
 323  0 return set.size();
 324    }
 325   
 326  4 public int getNumberOfNodes()
 327    {
 328  4 return set.size();
 329    }
 330   
 331  0 public Iterator iterate()
 332    {
 333  0 return null;
 334    }
 335   
 336  0 public void modifyElementCount(int difference)
 337    {
 338    }
 339   
 340  0 public void removeNodeEntry(NodeEntry entry)
 341    {
 342  0 throw new UnsupportedOperationException();
 343    }
 344   
 345    }
 346   
 347    }