package cn.bran.play;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import cn.bran.japid.template.ActionRunner;
import cn.bran.japid.template.RenderResult;
/**
* The class defines a cached adapter to invoke a piece of code.
*
* This class can be used in actions to run a piece of rendering code
* conditionally
*
* @author Bing Ran<bing_ran@hotmail.com>
*
*/
public abstract class CacheableRunner extends ActionRunner /*implements Externalizable*/{
// private Object[] key = null;
protected String ttlAbs = null;
public String keyString;
protected boolean noCache = true;
private boolean readThru;
/**
*
* @param ttl
* time to live for the cached item, format in "1s", "2mn", "3h",
* "4d" for example, "29d" max
* @param args
* objects to make a key from their string values
*/
public CacheableRunner(String ttl, Object... args) {
init(ttl, args);
}
public CacheableRunner() {
}
/**
* @param ttl
* @param args
*/
protected void init(String ttl, Object... args) {
if (args == null || args.length == 0)
this.noCache = true;
if (ttl == null || ttl.trim().length() == 0) {
this.noCache = true;
} else {
this.noCache = false;
if (ttl.startsWith("-")) {
this.ttlAbs = ttl.substring(1);
this.readThru = true;
} else {
this.ttlAbs = ttl;
}
}
this.keyString = buildKey(args);
}
/**
* a constructor that use a key generated from the current query string to store the render result
* @param ttl
*/
public CacheableRunner(String ttl) {
this(ttl, JapidController.genCacheKey());
}
/**
* the main entry point this action runner
*/
@Override
public RenderResult run() {
if (!shouldCache()) {
return render();
} else {
// cache in work
RenderResult rr = null;
if (!readThru) {
try {
rr = RenderResultCache.get(keyString);
if (rr != null)
return rr;
} catch (ShouldRefreshException e) {
// let's refresh the cache, flow through
}
}
try {
rr = render();
} catch (JapidResult e) {
rr = e.getRenderResult();
}
cacheResult(rr);
return (rr);
}
//
}
/**
* @return
*/
protected boolean shouldCache() {
// System.out.println("CacheableRunner: should cache: " + !noCache);
return !noCache;
}
/**
* @param rr1
*/
protected void cacheResult(RenderResult rr1) {
// System.out.println("CacheableRunner: put in cache");
RenderResultCache.set(keyString, rr1, ttlAbs);
}
/**
* @return
*/
public static String buildKey(Object[] keys) {
String keyString = "";
for (Object k : keys) {
keyString += ":" + String.valueOf(k);
}
if (keyString.startsWith(":"))
if ( keyString.length() > 1)
keyString = keyString.substring(1);
else
keyString = "";
return keyString;
}
/**
* the render method can either return a RenderResult or throw a JapidResult
*
* @return
*/
protected abstract RenderResult render();
public static void deleteCache(Object...objects) {
RenderResultCache.delete(buildKey(objects));
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(ttlAbs);
out.writeUTF(keyString);
out.writeBoolean(noCache);
out.writeBoolean(readThru);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
ttlAbs = in.readUTF();
keyString = in.readUTF();
noCache = in.readBoolean();
readThru = in.readBoolean();
}
}