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();

}

}

}

}

[6483 byte] By [tcn] at [2007-9-30 6:03:40]
# 1
You need to use the [code][/code] tags.
dubwai at 2007-7-1 19:35:51 > top of Java-index,Other Topics,Patterns & OO Design...
# 2
Judging from the interest it probably not important but nevertheless please note that the code above still contained some bugs JUnit did reveal.
tcn at 2007-7-1 19:35:51 > top of Java-index,Other Topics,Patterns & OO Design...
# 3
http://www.nitwit.de/vlh2/Comments (corrections to my understanding of the pattern..:) are explicitly appreciated....
tcn at 2007-7-1 19:35:51 > top of Java-index,Other Topics,Patterns & OO Design...