package com.alibaba.tamper.process.script.jexl; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.commons.jexl2.Expression; import org.apache.commons.jexl2.Interpreter; import org.apache.commons.jexl2.JexlContext; import org.apache.commons.jexl2.JexlEngine; import org.apache.commons.jexl2.MapContext; import org.apache.commons.jexl2.parser.JexlNode; import com.alibaba.tamper.process.script.ScriptContext; import com.alibaba.tamper.process.script.ScriptExecutor; import com.alibaba.tamper.process.script.lifecyle.DisposableScript; import com.alibaba.tamper.process.script.lifecyle.InitializingScript; /** * Jexl的script实现 * * @author jianghang 2011-5-25 下午08:08:45 */ public class JexlScriptExecutor implements ScriptExecutor { private static final int DEFAULT_CACHE_SIZE = 1000; private ScriptJexlEngine engine; private Map<String, Object> functions; private int cacheSize = DEFAULT_CACHE_SIZE; public JexlScriptExecutor(){ initialize(); } /** * 初始化function */ public void initialize() { if (cacheSize <= 0) {// 不考虑cacheSize为0的情况,强制使用LRU cache机制 cacheSize = DEFAULT_CACHE_SIZE; } functions = new HashMap<String, Object>(); engine = new ScriptJexlEngine(); engine.setCache(cacheSize); engine.setSilent(true); engine.setFunctions(functions); // 注册functions } public ScriptContext genScriptContext(Map<String, Object> context) { return new JexlScriptContext(context); } public Object evaluate(Map<String, Object> context, String script) { return evaluate(genScriptContext(context), script); } /** * <pre> * 1. 接受JexlScriptContext上下文 * 2. script针对对应name下的script脚本 * </pre> */ public Object evaluate(ScriptContext context, String script) { Expression expr = engine.createExpression(script); return expr.evaluate((JexlContext) context); } // ============================ setter / getter ============================ public void setCacheSize(int cacheSize) { this.cacheSize = cacheSize; } public void addFunction(String name, Object obj) { this.functions.put(name, obj); } public void disposeFunctions() { engine.disposeUsedFunctions(); } } /** * 定义为Jexl context * * @author jianghang 2011-5-25 下午07:57:59 */ class JexlScriptContext extends MapContext implements ScriptContext { public JexlScriptContext(Map map){ super(map); } } /** * 扩展jexl的实现,提供{@linkplain InitializingScript},{@linkplain DisposableScript}的管理 */ class ScriptJexlEngine extends JexlEngine { // 记录script执行过程中使用过的function private ThreadLocal<Set<Object>> usedFunctions = null; public ScriptJexlEngine(){ super(null, null, null, null); usedFunctions = new ThreadLocal<Set<Object>>() { protected Set<Object> initialValue() { return new HashSet<Object>(); // 线程安全,不全同步 } }; } protected Interpreter createInterpreter(JexlContext context) { if (context == null) { context = EMPTY_CONTEXT; } return new ScriptInterpreter(this, context); } class ScriptInterpreter extends Interpreter { public ScriptInterpreter(JexlEngine jexl, JexlContext aContext){ super(jexl, aContext); } protected Object resolveNamespace(String prefix, JexlNode node) { Object result = super.resolveNamespace(prefix, node); if (result != null) { boolean contains = usedFunctions.get().add(result);// 添加到使用记录中 if (contains && InitializingScript.class.isAssignableFrom(result.getClass())) {// 第一次添加 ((InitializingScript) result).initial();// 回调 } } return result; } } public void disposeUsedFunctions() { try { Set<Object> functions = usedFunctions.get(); for (Object function : functions) { if (function != null && DisposableScript.class.isAssignableFrom(function.getClass())) { ((DisposableScript) function).dispose();// 回调 } } } finally { usedFunctions.set(new HashSet<Object>());// 清空 } } }