package cn.bran.play;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import play.cache.Cached;
import cn.bran.japid.template.JapidRenderer;
import cn.bran.japid.template.RenderResult;
/**
* The class defines a cached adapter to invoke a Play controller action. A new instance
* must be used for each action invocation.
*
* This class can be used in template, indirectly by Japid engine.
*
* @author Bing Ran<bing_ran@hotmail.com>
*
*/
public abstract class CacheablePlayActionRunner extends CacheableRunner {
// String controllerActionString;
// private CacheFor cacheFor;
// private boolean gotFromCacheForCache;
// private String cacheForVal;
private Class<? extends JapidController> controllerClass;
private String actionName;
/**
*
* @param ttl
* @param args
* @deprecated use the other constructor that allow for better integration with CacheFor annotation
*/
public CacheablePlayActionRunner(String ttl, Object... args) {
super(ttl, args);
// if (args != null && args.length > 0) {
// // the first argument is the methodInvocation string, like myController.myMethod
// controllerActionString = (String) args[0];
// }
}
public CacheablePlayActionRunner(String ttl, Class<? extends JapidController> controllerClass, String actionName, Object... args) {
this.controllerClass = controllerClass;
this.actionName = actionName;
Object[] fullArgs = buildCacheKeyParts(controllerClass, actionName, args);
super.init(ttl, fullArgs);
// Object[] fullArgs = new Object[args.length + 2];
// System.arraycopy(args, 0, fullArgs, 0, args.length);
// fullArgs[args.length] = controllerClass.getName();
// fullArgs[args.length + 1] = actionName;
// super.init(ttl, fullArgs);
}
public CacheablePlayActionRunner(String ttl) {
super(ttl);
}
/**
* the action invocation to Play controller action, which must throw a
* JapidResult
*
* @throws JapidResult
*/
protected abstract JapidResult runPlayAction();
@Override
protected RenderResult render() {
Map<String, String> threadData = JapidController.threadData.get();
threadData.put(GlobalSettingsWithJapid.ACTION_METHOD, controllerClass.getName() + "." + actionName);
JapidResult jr = runPlayAction();
threadData.remove(GlobalSettingsWithJapid.ACTION_METHOD);
RenderResult rr = jr.getRenderResult();
return rr;
}
/**
* @return
*/
protected boolean shouldCache() {
fillCacheFor(controllerClass, actionName);
return super.shouldCache();
}
// /**
// * @param jr
// */
// @Override
// protected void cacheResult(RenderResult jr) {
// if (shouldCache()) {
// // already cached
// }
// else {
// super.cacheResult(jr);
// }
// }
// /**
// * check the cache for the action. The cache should have been caused by the CacheFor annotation
// *
// * @param class1
// * @param actionName
// * @deprecated the logic is not robust. new logic has been implemented in the super class.
// *
// */
// protected void checkActionCacheFor(Class<? extends JapidController> class1, String actionName) {
// fillCacheFor(class1, actionName);
//
// if (cacheForVal != null && cacheForVal.length() > 0) {
// String key = super.keyString;
// try {
//// Object v = play.cache.Cache.get(key);
// RenderResult v = RenderResultCache.get(key);
// if (v != null) {
// System.out.println("got from cache");
// this.gotFromCacheForCache = true;
// throw new JapidResult(v);
//// if (v instanceof JapidResult) {
//// throw ((JapidResult) v);
//// } else if (v instanceof CachedRenderResult) {
//// throw new JapidResult(((CachedRenderResult) v).rr);
//// } else {
////// throw new RuntimeException("got something from the cache but not sure what it is: "
////// + v.getClass().getName());
//// }
// }
// } catch (ShouldRefreshException e) {
// }
// return;
// }
// }
/**
* @param class1
* @param actionName
*/
private void fillCacheFor(Class<? extends JapidController> class1, String actionName) {
String className = class1.getName();
String cacheForKey = className + "_" + actionName;
Integer cacheForVal = (Integer) JapidRenderer.getCache().get(cacheForKey);
if (cacheForVal == null) {
// the cache has not been filled up yet.
Method[] mths = class1.getDeclaredMethods();
for (Method m : mths) {
if (m.getName().equalsIgnoreCase(actionName) && Modifier.isPublic(m.getModifiers())) {
Cached cacheFor = m.getAnnotation(Cached.class);
if (cacheFor == null) {
// well no annotation level cache spec
cacheForVal = 0;
}
else {
cacheForVal = cacheFor.duration();
}
JapidRenderer.getCache().put(cacheForKey, cacheForVal);
}
}
}
if (cacheForVal > 0){
super.noCache = false;
// only override ttlAbs if it's not specified.
if (super.ttlAbs == null || super.ttlAbs.length() == 0)
super.ttlAbs = cacheForVal + "s";
}
}
}