View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.rule;
17  
18  import org.apache.log4j.Logger;
19  import org.kuali.rice.core.api.exception.RiceIllegalStateException;
20  import org.kuali.rice.kew.api.KewApiServiceLocator;
21  import org.kuali.rice.kew.api.WorkflowRuntimeException;
22  import org.kuali.rice.kew.api.exception.WorkflowException;
23  import org.kuali.rice.kew.engine.RouteContext;
24  
25  import javax.script.ScriptEngine;
26  import javax.script.ScriptEngineManager;
27  import javax.script.ScriptException;
28  
29  /**
30   * A rule expression implementation that uses Bean Scripting Framework.
31   * The language is given by the type qualifier, e.g.:
32   * <expression type="BSF:groovy">...
33   *
34   * @author Kuali Rice Team (rice.collab@kuali.org)
35   */
36  //TODO: this should really be renamed since it is no longer using apache BSF
37  public class BSFRuleExpression implements RuleExpression {
38      private static final Logger LOG = Logger.getLogger(BSFRuleExpression.class);
39  
40      public RuleExpressionResult evaluate(Rule rule, RouteContext context) {
41          org.kuali.rice.kew.api.rule.RuleContract ruleDefinition = rule.getDefinition();
42          String name = "" + ruleDefinition.getName();
43          String type = ruleDefinition.getRuleExpressionDef().getType();
44          String lang = parseLang(type, "groovy");
45          String expression = ruleDefinition.getRuleExpressionDef().getExpression();
46          RuleExpressionResult result;
47          ScriptEngineManager factory = new ScriptEngineManager();
48          ScriptEngine engine = factory.getEngineByName(lang);
49          try {
50              declareBeans(engine, rule, context);
51              result = (RuleExpressionResult) engine.eval(expression);
52          } catch (ScriptException e) {
53              String details =  ( e.getLineNumber() >= 0 ?  " line: " + e.getLineNumber() + " column: " + e.getColumnNumber() : "" );
54              LOG.debug("Error evaluating rule '" + name + "' " + type +  " expression" + details + ": '" + expression + "'" + details, e);
55              throw new RiceIllegalStateException("Error evaluating rule '" + name + "' " + type + " expression" + details, e);
56          }
57          if (result == null) {
58              return new RuleExpressionResult(rule, false);
59          } else {
60              return result;
61          }
62      }
63  
64      /**
65       * Parses the language component from the type string
66       * @param type the type string
67       * @param deflt the default language if none is present in the type string
68       * @return the language component or null
69       */
70      protected String parseLang(String type, String deflt) {
71          int colon = type.indexOf(':');
72          if (colon > -1) {
73              return type.substring(colon + 1);
74          } else {
75              return deflt;
76          }
77      }
78  
79      /**
80       * Populates the BSFManager with beans that are accessible to BSF scripts.  May be overridden by
81       * subclasses.  The standard implementation exposes the rule and routeContext
82       * @param manager the BSFManager
83       * @param rule the current Rule object
84       * @param context the current RouteContext
85       */
86      protected void declareBeans(ScriptEngine engine, Rule rule, RouteContext context) throws ScriptException {
87          engine.put("rule", rule);
88          engine.put("routeContext", context);
89          engine.put("workflow", new WorkflowRuleAPI(context));
90      }
91  
92      /**
93       * A helper bean that is declared for use by BSF scripts.
94       * This functionality should really be part of a single internal API that can be exposed
95       * to various pieces of code that are plugged into KEW.  For comparison EDocLite also
96       * has its own such API that it exposes. 
97       */
98      protected static final class WorkflowRuleAPI {
99          private final RouteContext context;
100         WorkflowRuleAPI(RouteContext context) {
101             this.context = context;
102         }
103         /**
104          * Evaluates a named rule
105          * @param name the rule name
106          * @return the RuleExpressionResult
107          * @throws WorkflowException 
108          */
109         public RuleExpressionResult invokeRule(String name) throws WorkflowException {
110             org.kuali.rice.kew.api.rule.Rule rbv = KewApiServiceLocator.getRuleService().getRuleByName(name);
111             if (rbv == null) throw new WorkflowRuntimeException("Could not find rule named \"" + name + "\"");
112             Rule r = new RuleImpl(rbv);
113             return r.evaluate(r, context);
114         }
115     }
116 }