package com.easyooo.framework.rule.impl; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.easyooo.framework.rule.Language; import com.easyooo.framework.rule.Rule; import com.easyooo.framework.rule.RuleClassLoader; import com.easyooo.framework.rule.RuleException; import com.easyooo.framework.rule.RuleExecutor; import com.easyooo.framework.rule.groovy.GroovyRuleClassLoader; import com.easyooo.framework.rule.java.JavaRuleClassLoader; /** * Rule 类管理器 * * @author Killer */ public class RuleClassManager { protected final Logger logger = LoggerFactory.getLogger(getClass()); /** * 缓存规则及编译好的规则实例 * Key: rule id * Value: {@link CacheValue} */ private final ConcurrentHashMap<String, Future<CacheValue>> cache = new ConcurrentHashMap<>(); private static RuleClassManager instance; private RuleClassManager(){ super(); } public static synchronized RuleClassManager getInstance() { if (instance == null) { return (instance = new RuleClassManager()); } return instance; } /** * 获取一个规则实现,如果不存在,则动态编译成Class */ public RuleExecutor get(final Rule ruleObj)throws RuleException { if(ruleObj == null) return null; Future<CacheValue> future = cache.get(ruleObj.getRuleId()); if(future == null){ Callable<CacheValue> eval = new Callable<CacheValue>() { @Override public CacheValue call() throws Exception { try { RuleExecutor executor = loadRuleInstance(ruleObj); return new CacheValue(ruleObj, executor); } catch (RuleException e) { throw new ExecutionException(e); } } }; FutureTask<CacheValue> ft = new FutureTask<CacheValue>(eval); future = cache.putIfAbsent(ruleObj.getRuleId(), ft); if(future == null){ future = ft; ft.run(); } } try { CacheValue obj = future.get(); if(obj.rule.getVersion().equals(ruleObj.getVersion())){ return obj.executor; }else{ // 版本不一致,删除老版本,编译新版本 logger.debug(String.format("Check do not agree to release[%d,%d], recompile", obj.rule.getVersion(), ruleObj.getVersion())); uninstallRule(obj.rule); // 编译新的内容并设置到缓存 return get(ruleObj); } } catch (InterruptedException e) { cache.remove(ruleObj.getRuleId()); throw new RuleException(e); } catch (ExecutionException e) { cache.remove(ruleObj.getRuleId()); throw new RuleException("Load " + ruleObj.getRuleId() + " error", e); } } public RuleExecutor getIfExist(final Rule ruleObj)throws RuleException{ Future<CacheValue> future = cache.get(ruleObj.getRuleId()); if(null == future){ return null; } if(future.isDone()){ try { return future.get().executor; } catch (InterruptedException e) { throw new RuleException(e); } catch (ExecutionException igore) { } } return null; } /** * 卸载一个Rule,该方法一般不会主动调用 */ public boolean uninstallRule(final Rule ruleObj)throws RuleException{ cache.remove(ruleObj.getRuleId()); try { return new ScriptWriter(ruleObj).deleteClass(); } catch (IOException e) { throw new RuleException(e); } } private RuleExecutor loadRuleInstance(Rule rule)throws RuleException{ ScriptWriter sw = new ScriptWriter(rule); Class<?> clazz = sw.getScriptClass(); if(clazz == null){ RuleClassLoader rcl = switchClassLoader(rule.getLanguage()); if(rcl == null){ throw new RuleException("Can not find a matching loader"); } clazz = rcl.loadClass(rule); } RuleExecutor re = null; try { re = (RuleExecutor)clazz.newInstance(); } catch (Exception e) { throw new RuleException("Instantiate the script error", e); } return re; } /** * 验证语法是否通过 * * @param rule * @return * @throws RuleException */ public boolean verifyRuleSyntax(Rule rule)throws RuleException{ ScriptWriter sw = new ScriptWriter(rule); try { // 可能不存在,忽略该异常 sw.deleteClass(); } catch (IOException ig) { } // 没有异常意味着编译通过 try{ loadRuleInstance(rule); }finally{ try { sw.deleteClass(); } catch (IOException ig) { } } return true; } private class CacheValue{ public Rule rule; public RuleExecutor executor; public CacheValue(Rule rule, RuleExecutor executor){ this.rule = rule; this.executor = executor; } } /** * 目前仅支持两种编译成JVM的字节码加载器,如果需要其它支持则进行扩展, * JVM可以直接内置JS执行引擎,也可以通过命令行的方式编译成class文件, * 但Class内容不好控制,因此这里不适用classLoader的模式。 * * @param type * @return */ private RuleClassLoader switchClassLoader(Language type){ switch (type) { case GROOVY: return new GroovyRuleClassLoader(); case JAVA: return new JavaRuleClassLoader(); default: return null; } } }