ValueListHandler drawback and its solution
Hi!
Mabe I misunderstood the VLH pattern but to me it seems to have a major drawback: I requires a static value list. There are two potential problems with that: 1) memory 2) performance.
Two concrete examples:
You may fetch a huge amount of data from a database backend and you have to store all the information in the value list - no matter whether you need *all* data in the presensation tier or not.
Secondly, performance: I implemented a search using Lucene. Lucene returns a Hits object which contains references to the actuall docs that fit the query. Hits.doc(index) return the actual doc - and it fetches that doc only, when I call that method, i.e. when I need it. Consider a query that return 10.000 hits. I have to fetch all 10.000 hits in order to build the value list, but the user may likely only have a look at a few hits. This leads to a significant performance decrease.
So I came up with a simple ValueListSupplier that replaces the value list. It does fetch data only on demand. You pass an abritrary data object (in case of Lucene, a Hits object) to the supplier and an adapter that does create VOs (TOs):
Here example code:
Hits hits = searcher.search(qQuery);
SearchResultVLH result = new SearchResultVLH(new ValueListSupplier(hits)
{
protected ValueListSupplier.Adapter createAdapter()
{
return new ValueListSupplier.Adapter()
{
private final Log log = LogFactory.getLog(this.getClass());
public int size()
{
return ((Hits)valueList).length();
}
public Object get(int index)
{
try
{
Hits hits = (Hits)valueList;
Document doc = hits.doc(index);
float score = hits.score(index);
SearchResultVO vo = new SearchResultVO();
vo.setTitle(doc.get("title"));
vo.setSummary(doc.get("summary"));
vo.setUrl(doc.get("url"));
vo.setScore(score);
return vo;
}
catch(Exception e)
{
log.warn(e);
}
return null;
}
};
}
});
And the pattern:
/*
* Created on Apr 8, 2004
*/
package f4t.patterns.j2ee.vlh2;
import java.util.List;
/**
* @author tcn
*/
public interface IValueListIterator
{
public abstract int size();
public abstract void seek(int index);
public abstract void rewind();
public abstract Object current();
public abstract boolean hasNext();
public abstract boolean hasPrevious();
public abstract List next(int count);
public abstract List previous(int count);
}
/*
* Created on Apr 9, 2004
*/
package f4t.patterns.j2ee.vlh2;
import java.util.List;
/**
* @author tcn
*/
public class ValueListHandler implements IValueListIterator
{
protected ValueListSupplier supplier;
protected ValueListHandler(ValueListSupplier supplier)
{
this.supplier = supplier;
}
public int size()
{
return supplier.size();
}
public Object current()
{
return supplier.current();
}
public List previous(int count)
{
return supplier.previous(count);
}
public List next(int count)
{
return supplier.next(count);
}
public synchronized void seek(int index)
{
supplier.seek(index);
}
public void rewind()
{
seek(0);
}
public boolean hasNext()
{
return supplier.hasNext();
}
public boolean hasPrevious()
{
return supplier.hasPrevious();
}
}
/*
* Created on Apr 12, 2004
*/
package f4t.patterns.j2ee.vlh2;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Fetch data on demand.
*
* TODO junit test cases, check w/o seek(), previous()
*
* @author tcn
*/
public abstract class ValueListSupplier implements IValueListIterator
{
protected interface Adapter
{
public abstract int size();
public abstract Object get(int index);
}
private final Log log = LogFactory.getLog(this.getClass());
protected Object valueList;
protected Adapter adapter;
protected BitSet fetched;
protected List list;
protected int index;
public ValueListSupplier(Object valueList)
{
this.valueList = valueList;
this.adapter = createAdapter();
int size = size();
this.list = new ArrayList(size);
// Only SUN knows why... :-(
for (int i = 0; i < size; i++) list.add(null);
this.fetched = new BitSet(size);
this.index = 0;
}
protected abstract Adapter createAdapter();
public int size()
{
return adapter.size();
}
public boolean hasPrevious()
{
return index > 0;
}
public boolean hasNext()
{
return index + 1 < size();
}
/**
* @param index
*/
public synchronized void seek(int index)
{
this.index = index;
}
public void rewind()
{
seek(0);
}
// TODO first(), last()
public Object current()
{
checkConsistency(index, index + 1);
return list.get(index);
}
public List previous(int count)
{
int from = Math.max(0, index - count);
int to = index;
checkConsistency(from, to);
seek(from);
return list.subList(from, to);
}
public List next(int count)
{
int from = index;
int to = Math.min(size(), index + count);
checkConsistency(from, to);
seek(to);
return list.subList(from, to);
}
/**
* @param from
*starting index (inclusive)
* @param to
*ending index (exclusive)
*/
private void checkConsistency(int from, int to)
{
while ((from = fetched.nextClearBit(from)) < to)
{
log.debug("fetching #" + from);
try
{
list.set(from, adapter.get(from));
fetched.set(from);
}
catch (Exception e)
{
// TODO potential bug
e.printStackTrace();
}
}
}
}

