View Javadoc

1   /**
2    * Copyright 2005-2012 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.routemodule;
17  
18  import org.apache.log4j.Logger;
19  import org.jdom.Document;
20  import org.jdom.Element;
21  import org.kuali.rice.core.api.impex.xml.XmlConstants;
22  import org.kuali.rice.core.api.reflect.ObjectDefinition;
23  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
24  import org.kuali.rice.core.api.util.xml.XmlHelper;
25  import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
26  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
27  import org.kuali.rice.kew.engine.RouteContext;
28  import org.kuali.rice.kew.engine.node.RouteNodeInstance;
29  import org.kuali.rice.kew.rule.RuleBaseValues;
30  import org.kuali.rice.kew.rule.RuleExtensionBo;
31  import org.kuali.rice.kew.rule.RuleResponsibilityBo;
32  import org.kuali.rice.kew.rule.WorkflowRuleAttribute;
33  import org.kuali.rice.kew.rule.bo.RuleAttribute;
34  import org.kuali.rice.kew.rule.xmlrouting.GenericXMLRuleAttribute;
35  import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
36  import org.kuali.rice.kew.service.KEWServiceLocator;
37  import org.kuali.rice.kew.api.KewApiConstants;
38  import org.kuali.rice.kew.xml.RuleXmlParser;
39  
40  import javax.xml.xpath.XPath;
41  import javax.xml.xpath.XPathConstants;
42  import java.io.ByteArrayInputStream;
43  import java.util.ArrayList;
44  import java.util.List;
45  
46  
47  /**
48   * A RouteModule that generates requests for responsibilities statically defined
49   * in the config block of the node.
50   * @author Kuali Rice Team (rice.collab@kuali.org)
51   */
52  public class InlineRequestsRouteModule extends FlexRMAdapter {
53      private static final Logger LOG = Logger.getLogger(InlineRequestsRouteModule.class);
54  
55      /**
56       * This overridden method is used to decipher the inline xpath and responsibilities of a route node definition and use
57       * them to create action reqeusts
58       * 
59       * @see org.kuali.rice.kew.routemodule.FlexRMAdapter#findActionRequests(org.kuali.rice.kew.engine.RouteContext)
60       */
61      @Override
62      public List<ActionRequestValue> findActionRequests(RouteContext context) throws Exception {
63          // comment this out while implementing the meta-rules stuff
64          // re-implement later
65          List<ActionRequestValue> actionRequests = new ArrayList<ActionRequestValue>();
66          RouteNodeInstance currentNode = context.getNodeInstance();
67          String contentFragment = currentNode.getRouteNode().getContentFragment();
68          // parse with JDOM to reuse RuleXmlParser
69          Document doc = XmlHelper.trimSAXXml(new ByteArrayInputStream(contentFragment.getBytes()));
70          Element root = doc.getRootElement();
71          List<String> ruleAttributeNames = new ArrayList<String>();
72          List<String> ruleAttributeClassNames = new ArrayList<String>();
73          List<String> xpathExpressions = new ArrayList<String>();
74          // get the list of ruleAttributes to use
75          Element ruleAttributes = root.getChild("ruleAttributes");
76          if (ruleAttributes != null) {
77              for (Object o : ruleAttributes.getChildren("name")) {
78                  Element e = (Element) o;
79                  ruleAttributeNames.add(e.getText());
80              }
81              for (Object o : ruleAttributes.getChildren("className")) {
82                  Element e = (Element) o;
83                  ruleAttributeClassNames.add(e.getText());
84              }
85          }
86          // get the list of xpath expressions to verify
87          for (Object o: root.getChildren("match")) {
88              Element e = (Element) o;
89              xpathExpressions.add(e.getText());
90          }
91          if ( (ruleAttributeNames.isEmpty()) && (ruleAttributeClassNames.isEmpty()) && (xpathExpressions.isEmpty()) ) {
92              throw new RuntimeException("Match xpath expression not specified (should be parse-time exception...)");
93          }
94  
95          List<WorkflowRuleAttribute> attributes = new ArrayList<WorkflowRuleAttribute>();
96          for (String attributeName : ruleAttributeNames) {
97              attributes.add(getRuleAttributeByName(attributeName));
98          }
99          for (String attributeClassName : ruleAttributeClassNames) {
100             attributes.add(getRuleAttributeByClassName(attributeClassName));
101         }
102         
103         // at this point if we have no xpath expressions or attributes we cannot match
104         if (attributes.isEmpty() && xpathExpressions.isEmpty()) {
105             return actionRequests;
106         }
107         
108         Boolean match = Boolean.TRUE;
109         if (!xpathExpressions.isEmpty()) {
110             XPath xpath = XPathHelper.newXPath();
111             for (String xpathExpression : xpathExpressions) {
112                 match &= (Boolean) xpath.evaluate(xpathExpression, context.getDocumentContent().getDocument(), XPathConstants.BOOLEAN);
113             }
114         }
115         for (WorkflowRuleAttribute workflowAttribute : attributes) {
116             // no rule extensions to pass in below because we have no rule... simple attribute matching only
117             match &= workflowAttribute.isMatch(context.getDocumentContent(), new ArrayList<RuleExtensionBo>());
118         }
119         
120         if (match.booleanValue()) {
121 //            LOG.debug("Expression '" + xpathExpression + "' matched document '" + context.getDocumentContent().getDocContent() + "'");
122         } else {
123             // return an empty list because we didn't find a match using the given xpath
124 //            LOG.debug("Expression '" + xpathExpression + "' did NOT match document '" + context.getDocumentContent().getDocContent() + "'");
125             return actionRequests;
126         }
127 
128         List<org.kuali.rice.kew.api.rule.RuleResponsibility> responsibilities = new ArrayList<org.kuali.rice.kew.api.rule.RuleResponsibility>();
129         RuleXmlParser parser = new RuleXmlParser();
130         ActionRequestFactory arf = new ActionRequestFactory(context.getDocument(), currentNode);
131         // this rule is only used to obtain description, forceAction flag, and the rulebasevalues id, which may be null
132         RuleBaseValues fakeRule = new RuleBaseValues();
133         fakeRule.setName("fakeRule");
134         fakeRule.setActive(Boolean.TRUE);
135         fakeRule.setCurrentInd(Boolean.TRUE);
136         fakeRule.setDescription("a fake rule");
137         fakeRule.setForceAction(Boolean.TRUE);
138         fakeRule.setId(null);
139 
140         for (Object o: root.getChildren("responsibility", XmlConstants.RULE_NAMESPACE)) {
141             Element e = (Element) o;
142             RuleResponsibilityBo responsibility = parser.parseResponsibility(e, fakeRule);
143             responsibility.setResponsibilityId(KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID);
144             responsibilities.add(org.kuali.rice.kew.api.rule.RuleResponsibility.Builder.create(responsibility).build());
145         }
146         if (responsibilities.isEmpty()) {
147             throw new RuntimeException("No responsibilities found on node " + currentNode.getName());
148         }
149 
150         makeActionRequests(arf, responsibilities, context, RuleBaseValues.to(fakeRule), context.getDocument(), null, null);
151         actionRequests.addAll(arf.getRequestGraphs());
152         return actionRequests;
153     }
154     
155     @Override
156     public String toString() {
157         return "InlineRequestsRouteModule";
158     }
159 
160     private WorkflowRuleAttribute getRuleAttributeByName(String ruleAttributeName) {
161         return materializeRuleAttribute(KEWServiceLocator.getRuleAttributeService().findByName(ruleAttributeName));
162     }
163     
164     private WorkflowRuleAttribute getRuleAttributeByClassName(String ruleAttributeClassName) {
165         return materializeRuleAttribute(KEWServiceLocator.getRuleAttributeService().findByClassName(ruleAttributeClassName));
166     }
167     
168     private WorkflowRuleAttribute materializeRuleAttribute(RuleAttribute ruleAttribute) {
169         if (ruleAttribute != null) {
170             if (KewApiConstants.RULE_ATTRIBUTE_TYPE.equals(ruleAttribute.getType())) {
171                 ObjectDefinition objDef = new ObjectDefinition(ruleAttribute.getResourceDescriptor(), ruleAttribute.getApplicationId());
172                 return (WorkflowRuleAttribute) GlobalResourceLoader.getObject(objDef);
173             } else if (KewApiConstants.RULE_XML_ATTRIBUTE_TYPE.equals(ruleAttribute.getType())) {
174                 ObjectDefinition objDef = new ObjectDefinition(ruleAttribute.getResourceDescriptor(), ruleAttribute.getApplicationId());
175                 WorkflowRuleAttribute workflowAttribute = (WorkflowRuleAttribute) GlobalResourceLoader.getObject(objDef);
176                 //required to make it work because ruleAttribute XML is required to construct custom columns
177                 ((GenericXMLRuleAttribute) workflowAttribute).setExtensionDefinition(RuleAttribute.to(ruleAttribute));
178                 return workflowAttribute;
179             }
180         }
181         return null;
182     }
183     
184 }