/*
* JBoss, Home of Professional Open Source.
* Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.cache.eviction;
import org.jboss.cache.Fqn;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* LFUQueue EvictionQueue implementation for LFU Policy.
*
* The queue is sorted in least frequently used order.
*
* @author Daniel Huang (dhuang@jboss.org)
* @version $Revision$
*/
public class LFUQueue implements SortedEvictionQueue
{
private Map nodeMap;
private LinkedList evictionList;
private Comparator comparator;
private Set removalQueue;
private int numElements = 0;
protected LFUQueue()
{
nodeMap = new HashMap();
comparator = new LFUComparator();
evictionList = new LinkedList();
removalQueue = new HashSet();
}
/**
* Return the first node to evict.
*
* This method will return the least frequently used entry in the queue.
*/
public NodeEntry getFirstNodeEntry()
{
try
{
NodeEntry ne;
while ((ne = evictionList.getFirst()) != null)
{
if (removalQueue.contains(ne))
{
evictionList.removeFirst();
removalQueue.remove(ne);
}
else
{
break;
}
}
return ne;
}
catch (NoSuchElementException e)
{
return null;
}
}
public NodeEntry getNodeEntry(Fqn fqn)
{
return nodeMap.get(fqn);
}
public NodeEntry getNodeEntry(String fqn)
{
return this.getNodeEntry(Fqn.fromString(fqn));
}
public boolean containsNodeEntry(NodeEntry entry)
{
Fqn fqn = entry.getFqn();
return this.getNodeEntry(fqn) != null;
}
public void removeNodeEntry(NodeEntry entry)
{
NodeEntry ne = nodeMap.remove(entry.getFqn());
if (ne != null)
{
// don't remove directly from the LinkedList otherwise we will incur a O(n) = n
// performance penalty for every removal! In the prune method for LFU, we will iterate the LinkedList through ONCE
// doing a single O(n) = n operation and removal. This is much preferred over running O(n) = n every single time
// remove is called. There is also special logic in the getFirstNodeEntry that will know to check
// the removalQueue before returning.
this.removalQueue.add(ne);
/* if(!evictionList.remove(ne)) {
throw new RuntimeException("");
} */
this.numElements -= ne.getNumberOfElements();
}
}
public void addNodeEntry(NodeEntry entry)
{
if (!this.containsNodeEntry(entry))
{
Fqn fqn = entry.getFqn();
entry.queue = this;
nodeMap.put(fqn, entry);
evictionList.add(entry);
this.numElements += entry.getNumberOfElements();
}
}
public int getNumberOfNodes()
{
return nodeMap.size();
}
public int getNumberOfElements()
{
return this.numElements;
}
public void clear()
{
nodeMap.clear();
evictionList.clear();
removalQueue.clear();
this.numElements = 0;
}
public void resortEvictionQueue()
{
Collections.sort(evictionList, comparator);
}
public void modifyElementCount(int difference)
{
this.numElements += difference;
}
protected void prune()
{
Iterator it = this.iterator();
while (it.hasNext() && removalQueue.size() > 0)
{
if (removalQueue.remove(it.next()))
{
it.remove();
}
}
}
protected final List getEvictionList()
{
return this.evictionList;
}
protected final Set getRemovalQueue()
{
return this.removalQueue;
}
public Iterator iterator()
{
return evictionList.iterator();
}
/**
* Comparator class for LFU.
*
* This class will sort the eviction queue in the correct eviction order.
* The top of the list should evict before the bottom of the list.
*
* The sort is based on ascending order of nodeVisits.
*
* Note: this class has a natural ordering that is inconsistent with equals as defined by the java.lang.Comparator
* contract.
*/
protected static class LFUComparator implements Comparator
{
public int compare(NodeEntry ne1, NodeEntry ne2)
{
if (ne1.equals(ne2))
{
return 0;
}
int neNodeHits = ne1.getNumberOfNodeVisits();
int ne2NodeHits = ne2.getNumberOfNodeVisits();
if (neNodeHits > ne2NodeHits)
{
return 1;
}
else if (neNodeHits < ne2NodeHits)
{
return -1;
}
else if (neNodeHits == ne2NodeHits)
{
return 0;
}
throw new RuntimeException("Should never reach this condition");
}
}
}