A Java open source memoization library to cache the results of slow methods using annotations and a pluggable cache interface.
| By: | Abdul Habra |
| Email: | ahabra@yahoo.com |
| URL: | www.tek271.com |
| Version: | 1.01 |
| Date: | 2007.03.05 |
Tek271 Memoizer Java Docs.
tek271.memoize.2007.03.05.zip: Source, JAR, JavaDocs.
This software is open source, free, and uses LGPL license.
| Old versions | |
|---|---|
| Version | Download |
| 1.0 | tek271.memoize.2007.03.01.zip |
If a function produces the same output given the same inputs, and if this function is slow, it makes sense for the function to cache its outputs to avoid repeated evaluations. This caching behavior is called Memoization (yes there is no r).
For example consider a method that reads some authorization string from database for a given login:
// This is not ideal code, it is meant only to show a concept
public String readAuthorizationFromDb(String loginId) {
Connection con= getDbConnection(); // perhaps pooled
String sql= "select authorization from security where login_id='" + loginId + "'";
String auth= readFromDb(con, sql); // assume we have such a method
con.close();
return auth;
}
If this function is called many times, with the same loginId, a programmer may consider this kind of optimization:
Map cache= new HashMap(); // This is not ideal code, it is meant only to show a concept public String readAuthorizationFromDb(String loginId) { if (cache.containsKey(loginId)) { return (String) cache.get(loginId); } Connection con= getDbConnection(); // perhaps pooled String sql= "select authorization from security where login_id='" + loginId + "'"; String auth= readFromDb(con, sql); // assume we have such a method con.close(); cache.put(loginId, auth); return auth; }
This solution could work for a simple program, however it suffers from several problems such as:
loginIds, the size of map can increase and run out of memory.loginId is
put in the map it stays there, if the database contents change, there is no way to update
the content of the map besides restarting the program.One possible solution is to use Aspects to cache this kind of method. However, the solution provided here uses standard Java, and is lighter weight. The following is the method's code using Tek271 Memoizer:
// This is not ideal code, it is meant only to show a concept
@Remember
public String readAuthorizationFromDb(String loginId) {
Connection con= getDbConnection(); // perhaps pooled
String sql= "select authorization from security where login_id='" + loginId + "'";
String auth= readFromDb(con, sql); // assume we have such a method
con.close();
return auth;
}
As you can see, the only needed addition is to add the @Remember annotation
before the method's declaration.
cglib-nodep-2.2_beta1.jar. Available
from http://cglib.sourceforge.net. It is also
included in this project's download.tek271.memoize.jar.import com.tek271.memoize.Remember;
public class ExpensiveCalcs {
@Remember
public String slowMethod(String param1) {
System.out.println("inside slowMethod(" + param1 + ")");
return param1 + param1;
}
}
Now, to use the slowMethod():
import com.tek271.memoize.RememberFactory;
...
ExpensiveCalcs ec= (ExpensiveCalcs) RememberFactory.createProxy(ExpensiveCalcs.class);
String s= ec.slowMethod("hello");
Notice how you use RememberFactory.createProxy() to create instances of
ExpensiveCalcs instead of using a constructor.
@Remember's AttributesThe @Remember annotation has several attributes that can be used to modify
its default behavior.
maxSize
The maximum number of method results to cache. The default is 128. For example, to change
the maxSize to 1000:
@Remember(maxSize=1000)
timeToLive
The period of time after which, cached return values of the method will expire. The default
is 2 minutes. This attribute works in conjunction with the timeUnit attribute.
timeUnit
The unit of time for the timeToLive attribute. The default is Minute. This is an enumerations
with possible values of {MILLI, SECOND, MINUTE, HOUR}. For example, to change the timeToLive
to 4 hours:
@Remember(timeToLive=4, timeUnit=TimeUnitEnum.HOUR)
excludedParametersIndex
Method parameters that should NOT be used as part of the cache's key. The excluded parameters
are indicated in an int array. The java reflection API does not provide access
to method's parameters names, hence we use index. The index of the first parameter is zero.
For example assume that you have a method with three parameters:
@Remember
String readUsersAddress(Connection con, String city, String state) {
...
}
To memoize this method, you realize that the connection parameter should not impact the return value of the method:
@Remember(excludedParametersIndex={0})
String readUsersAddress(Connection con, String city, String state) {
...
}
The Tek271 Memoizer stores its cached values using a light-weight implementation of a cache
store using java.util.LinkedHashMap class. However, you can use any other caching
software by implementing the provided ICacheFactory and ICache interfaces
and using this factory method:
RememberFactory.createProxy(Class targetClass, ICacheFactory cacheFactory)
An implementation that uses Ehcache is available.
Version 1.01, 2007.03.05
Some JavaDocs fixes and spelling mistakes.
Version 1.0, 2007.03.01
First public release
| Page Last Updated 2007.03.05 |
|