package org.atricore.idbus.idojos.virtualidentitystore.scripting; import org.apache.bsf.BSFException; import org.apache.bsf.BSFManager; import org.apache.bsf.util.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.util.*; /** * Scripting Rule Engine based on the Bean Scripting Framework. * * @author <a href="mailto:gbrigand@josso.org">Gianluca Brigandi</a> * @version $Id: ScriptingRuleEngine.java 1644 2010-07-27 19:31:39Z sgonzalez $ */ public class ScriptingRuleEngine { private static final Log logger = LogFactory.getLog(ScriptingRuleEngine.class); /** * Holds the "compiled" scripts and their information */ protected static Map scripts = new Hashtable(); public ScriptingRuleExecutionOutcome execute(String ruleScriptName, Collection<ScriptingRuleParameter> ruleParameters) throws Exception { BSFManager bsfManager = new BSFManager(); ScriptingRuleExecutionOutcome outcome = new ScriptingRuleExecutionOutcomeImpl(); String parsedScriptName = parseScriptName(ruleScriptName, bsfManager); Script script = loadScript(parsedScriptName); bsfManager.declareBean("outcome", outcome, ScriptingRuleExecutionOutcomeImpl.class); bsfManager.declareBean("log", logger, Log.class); for (Iterator<ScriptingRuleParameter> scriptingRuleParameterIterator = ruleParameters.iterator(); scriptingRuleParameterIterator.hasNext();) { ScriptingRuleParameter scriptingRuleParameter = scriptingRuleParameterIterator.next(); bsfManager.declareBean(scriptingRuleParameter.getName(), scriptingRuleParameter.getValue(), scriptingRuleParameter.getType()); } bsfManager.exec(script.lang, script.file.getCanonicalPath(), 0, 0, script.string); return outcome; } public ScriptingRuleExecutionOutcome evaluate(String ruleExpression, String language, Collection<ScriptingRuleParameter> ruleParameters) throws Exception { BSFManager bsfManager = new BSFManager(); ScriptingRuleExecutionOutcome outcome = new ScriptingRuleExecutionOutcomeImpl(); bsfManager.declareBean("outcome", outcome, ScriptingRuleExecutionOutcomeImpl.class); bsfManager.declareBean("log", logger, Log.class); for (Iterator<ScriptingRuleParameter> scriptingRuleParameterIterator = ruleParameters.iterator(); scriptingRuleParameterIterator.hasNext();) { ScriptingRuleParameter scriptingRuleParameter = scriptingRuleParameterIterator.next(); bsfManager.declareBean(scriptingRuleParameter.getName(), scriptingRuleParameter.getValue(), scriptingRuleParameter.getType()); } bsfManager.eval(language, "<unknown>", 0, 0, ruleExpression); return outcome; } /** * Parses the script name and puts any url parameters in the context * * @param url The script url consisting of a path and optional * parameters * @param manager The BSF manager to declare new parameters in * @return The name of the script to execute * @throws Exception If something goes wrong */ protected String parseScriptName(String url, BSFManager manager) throws Exception { if (logger.isDebugEnabled()) { logger.debug("Parsing " + url); } String name = null; if (url != null) { String[] parsed = split(url, "?"); name = parsed[0]; if (parsed.length == 2) { if (logger.isDebugEnabled()) { logger.debug("Found a query string"); } String[] args = split(parsed[1], "&"); for (int x = 0; x < args.length; x++) { String[] param = split(args[x], "="); Object o = manager.lookupBean(param[0]); if (o != null) { logger.warn("BSF variable " + param[0] + " already exists"); param[0] = "_" + param[0]; } manager.declareBean(param[0], param[1], String.class); if (logger.isDebugEnabled()) { logger.debug("Registering param " + param[0] + " with value " + param[1]); } } } else { if (logger.isDebugEnabled()) { logger.debug("No query string:" + parsed.length); } } } return name; } /** * Loads the script from cache if possible. Reloads if the script has been * recently modified. * * @param name The name of the script * @return The script object */ protected Script loadScript(String name) { synchronized (scripts) { Script script = (Script) scripts.get(name); if (script == null) { script = new Script(); URL scriptUrl = ScriptingRuleEngine.class.getClassLoader().getResource(name); try { script.file = new File(scriptUrl.toURI()); } catch (URISyntaxException e) { script.file = new File(scriptUrl.getPath()); } try { script.lang = BSFManager.getLangFromFilename(script.file.getName()); } catch (BSFException ex) { logger.warn(ex, ex); } } boolean reloadScript = false; long scriptLastModified = script.file.lastModified(); if (scriptLastModified > script.timeLastLoaded) { if (logger.isDebugEnabled()) { logger.debug("Loading updated or new script: " + script.file.getName()); } reloadScript = true; } if (reloadScript || script.string == null) { synchronized (this) { if (reloadScript || script.string == null) { script.timeLastLoaded = System.currentTimeMillis(); try { script.string = IOUtils.getStringFromReader(new FileReader(script.file)); } catch (IOException ex) { logger.error("Unable to load script", ex); } } } } return script; } } /** * Splits a line with the given delimiter * * @param line The line to split * @param delimiter The string to split with * @return An array of substrings */ protected static String[] split(String line, String delimiter) { if (line == null || "".equals(line)) { return new String[]{}; } List lst = new ArrayList(); for (Enumeration e = new StringTokenizer(line, delimiter); e.hasMoreElements();) { lst.add(e.nextElement()); } String[] ret = new String[lst.size()]; return (String[]) lst.toArray(ret); } /** * Represents a saved script */ class Script { /** * The script file */ public File file; /** * The language the script is in */ public String lang = null; /** * The time when the script was last used */ public long timeLastLoaded = 0; /** * The contents of the script file */ public String string = null; } }