/** * Copyright (c) 2009 - 2012 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.policy.js; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.Undefined; import org.mozilla.javascript.Wrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * JsRunner - Responsible for running the javascript rules methods in all * namespaces. * Used by the various "Rules" classes. */ public class JsRunner { private static Logger log = LoggerFactory.getLogger(JsRunner.class); private Object rulesNameSpace; private String namespace; private Scriptable scope; private boolean initialized = false; public JsRunner(Scriptable scope) { this.scope = scope; } /** * initialize the javascript rules for the provided namespace. you must run * this * before trying to run a javascript rule or method. * * @param namespace the javascript rules namespace containing the rules type * you want */ public void init(String namespace) { this.namespace = namespace; if (!initialized) { Context context = Context.enter(); try { Object func = ScriptableObject.getProperty(scope, namespace); this.rulesNameSpace = unwrapReturnValue(((Function) func) .call(context, scope, scope, Context.emptyArgs)); this.initialized = true; } catch (RhinoException ex) { this.initialized = false; throw new RuleParseException(ex); } finally { Context.exit(); } } } public void reinitTo(String namespace) { initialized = false; init(namespace); } Object unwrapReturnValue(Object result) { if (result instanceof Wrapper) { result = ((Wrapper) result).unwrap(); } return result instanceof Undefined ? null : result; } @SuppressWarnings("unchecked") public <T> T invokeMethod(String method) throws NoSuchMethodException, RhinoException { Scriptable localScope = Context.toObject(this.rulesNameSpace, scope); Object func = ScriptableObject.getProperty(localScope, method); if (!(func instanceof Function)) { throw new NoSuchMethodException( "no such javascript method: " + method); } Context context = Context.enter(); try { return (T) unwrapReturnValue(((Function) func).call(context, scope, localScope, Context.emptyArgs)); } finally { Context.exit(); } } @SuppressWarnings("unchecked") public <T> T invokeMethod(String method, JsContext context) throws NoSuchMethodException, RhinoException { context.applyTo(scope); return (T) invokeMethod(method); } public <T> T invokeRule(String ruleName) { log.debug("Running rule: {} in namespace: {}", ruleName, namespace); T returner = null; try { returner = this.invokeMethod(ruleName); } catch (NoSuchMethodException ex) { log.info("No rule found: {} in namespace: {}", ruleName, namespace); } catch (RhinoException ex) { throw new RuleExecutionException(ex); } return returner; } public <T> T invokeRule(String ruleName, JsContext context) { context.applyTo(scope); return invokeRule(ruleName); } public <T extends Object> T runJsFunction(Class<T> clazz, String function, JsContext context) { T returner = null; try { returner = invokeMethod(function, context); } catch (NoSuchMethodException e) { log.warn("No javascript method found: {}", function); } catch (RhinoException e) { throw new RuleExecutionException(e); } return returner; } }