Decorate a Map
with LazyMap
.
Attempting to retrieve a value with a key that is not in a
Map
decorated with LazyMap
will
trigger the creation of a value by a Transformer
associated with this LazyMap
. The following
example decorates a HashMap
with a
Transformer
that reverses strings; when a key is
requested, a value is created and put into the Map
using this Transformer
:
import java.util.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.lang.StringUtils;
// Create a Transformer to reverse strings - defined below
Transformer reverseString = new Transformer( ) {
public Object transform( Object object ) {
String name = (String) object;
String reverse = StringUtils.reverse( name );
return reverse;
}
}
// Create a LazyMap called lazyNames, which uses the above Transformer
Map names = new HashMap( );
Map lazyNames = LazyMap.decorate( names, reverseString );
// Get and print two names
String name = (String) lazyNames.get( "Thomas" );
System.out.println( "Key: Thomas, Value: " + name );
name = (String) lazyNames.get( "Susan" );
System.out.println( "Key: Susan, Value: " + name );
Whenever get( )
is called, the decorated
Map
passes the requested key to a
Transformer
, and, in this case, a reversed string
is put into a Map
as a value. The previous example
requests two strings and prints the following output:
Key: Thomas, Value: samohT Key: Susan, Value: nasuS
LazyMap
works best when a key is a symbol or an
abbreviation for a more complex object. If you are working with a
database, you could create a LazyMap
that
retrieves objects by a primary key value; in this case, the
Transformer
simply retrieves a record from a
database table using the supplied key. Another example that springs
to mind is a stock quote; in the stock exchange, a company is
represented as a series of characters:
“YHOO” represents the company
Yahoo!, Inc., and “TSC” represents
TheStreet.com. If your system deals with a quote feed, use a
LazyMap
to cache frequently used entries. Example 5-17 uses a LazyMap
to create a
cache populated on demand, and it also demonstrates the
LRUMap
—a fixed-size implementation of the
Map
introduced in Recipe 5.17.
Example 5-17. Example using a LazyMap
package com.discursive.jccook.collections.lazy; import java.net.URL; import java.util.Map; import org.apache.commons.collections.map.LRUMap; import org.apache.commons.collections.map.LazyMap; public class LazyMapExample { Map stockQuotes; public static void main(String[] args) throws Exception { LazyMapExample example = new LazyMapExample( ); example.start( ); } public void start( ) throws Exception {StockQuoteTransformer sqTransformer = new StockQuoteTransformer( );
sqTransformer.setQuoteURL( new URL("http://quotes.company.com") );
sqTransformer.setTimeout( 500 );
// Create a Least Recently Used Map with max size = 5
stockQuotes = new LRUMap( 5 );
// Decorate the LRUMap with the StockQuoteTransformer
stockQuotes = LazyMap.decorate( stockQuotes, sqTransformer );
// Now use some of the entries in the cache Float price = (Float) stockQuotes.get( "CSCO" ); price = (Float) stockQuotes.get( "MSFT" ); price = (Float) stockQuotes.get( "TSC" ); price = (Float) stockQuotes.get( "TSC" ); price = (Float) stockQuotes.get( "LU" ); price = (Float) stockQuotes.get( "P" ); price = (Float) stockQuotes.get( "P" ); price = (Float) stockQuotes.get( "MSFT" ); price = (Float) stockQuotes.get( "LU" ); // Request another price to the Map, this should kick out the LRU item. price = (Float) stockQuotes.get( "AA" ); // CSCO was the first price requested, it is therefore the // least recently used. if( !stockQuotes.containsKey("CSCO") ) { System.out.println( "As expected CSCO was discarded" ); } } }
The Transformer
in Example 5-17 is
an object that takes a string and hits a URL using Jakarta
HttpClient—a utility introduced in Chapter 11. Every time a new symbol is encountered,
this Transformer
creates another thread with a
timeout and hits a “quote server”
that is configured to return the latest price for that
company’s symbol. Example 5-18
defines a
StockQuoteTransformer
that retrieves a quote by passing a stock symbol as a URL parameter.
Example 5-18. A StockQuoteTransformer
package com.discursive.jccook.collections.lazy; import java.net.URL; import org.apache.commons.collections.Transformer; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpURL; import org.apache.commons.httpclient.methods.GetMethod; public class StockQuoteTransformer implements Transformer { protected URL quoteURL; protected long timeout; public Object transform(Object symbol) { QuoteRetriever retriever = new QuoteRetriever( (String) symbol ); try { Thread retrieveThread = new Thread( retriever ); retrieveThread.start( ); retrieveThread.join( timeout ); } catch( InterruptedException ie ) { System.out.println( "Quote request timed out."); } return retriever.getResult( ); } public URL getQuoteURL( ) { return quoteURL; } public void setQuoteURL(URL url) { quoteURL = url; } public long getTimeout( ) { return timeout; } public void setTimeout(long l) { timeout = l; } public class QuoteRetriever implements Runnable { private String symbol; private Float result = new Float( Float.NaN ); public QuoteRetriever(String symbol) { this.symbol = symbol; } public Float getResult( ) { return result; } public void run( ) { HttpClient client = new HttpClient( ); try { HttpURL url = new HttpURL( quoteURL.toString( ) ); url.setQuery( "symbol", symbol ); GetMethod getMethod = new GetMethod( url.toString( ) ); client.executeMethod( getMethod ); String response = getMethod.getResponseBodyAsString( ); result = new Float( response ); } catch( Exception e ) { System.out.println( "Error retrieving quote" ); } } } }
The point of this example is to demonstrate the power of decorating
an LRUMap
with LazyMap
and to
write a Transformer
that can fetch a piece of data
from another server, not that the
StockQuoteTransformer
uses Jakarta HttpClient.
For more information about Jakarta HttpClient,
see Chapter 11. For more information about the
LRUMap
, see Recipe 5.17.
18.226.172.200