001 /** 002 * Copyright 2005-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.kew.routemodule; 017 018 import org.apache.log4j.Logger; 019 import org.jdom.Document; 020 import org.jdom.Element; 021 import org.kuali.rice.core.api.impex.xml.XmlConstants; 022 import org.kuali.rice.core.api.reflect.ObjectDefinition; 023 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 024 import org.kuali.rice.core.api.util.xml.XmlHelper; 025 import org.kuali.rice.kew.actionrequest.ActionRequestFactory; 026 import org.kuali.rice.kew.actionrequest.ActionRequestValue; 027 import org.kuali.rice.kew.api.KewApiServiceLocator; 028 import org.kuali.rice.kew.api.extension.ExtensionDefinition; 029 import org.kuali.rice.kew.api.rule.RuleExtension; 030 import org.kuali.rice.kew.engine.RouteContext; 031 import org.kuali.rice.kew.engine.node.RouteNodeInstance; 032 import org.kuali.rice.kew.rule.RuleBaseValues; 033 import org.kuali.rice.kew.rule.RuleExtensionBo; 034 import org.kuali.rice.kew.rule.RuleResponsibilityBo; 035 import org.kuali.rice.kew.rule.WorkflowRuleAttribute; 036 import org.kuali.rice.kew.rule.bo.RuleAttribute; 037 import org.kuali.rice.kew.rule.xmlrouting.GenericXMLRuleAttribute; 038 import org.kuali.rice.kew.rule.xmlrouting.XPathHelper; 039 import org.kuali.rice.kew.service.KEWServiceLocator; 040 import org.kuali.rice.kew.api.KewApiConstants; 041 import org.kuali.rice.kew.xml.RuleXmlParser; 042 043 import javax.xml.xpath.XPath; 044 import javax.xml.xpath.XPathConstants; 045 import java.io.ByteArrayInputStream; 046 import java.util.ArrayList; 047 import java.util.Collections; 048 import java.util.List; 049 050 051 /** 052 * A RouteModule that generates requests for responsibilities statically defined 053 * in the config block of the node. 054 * @author Kuali Rice Team (rice.collab@kuali.org) 055 */ 056 public class InlineRequestsRouteModule extends FlexRMAdapter { 057 private static final Logger LOG = Logger.getLogger(InlineRequestsRouteModule.class); 058 059 /** 060 * This overridden method is used to decipher the inline xpath and responsibilities of a route node definition and use 061 * them to create action reqeusts 062 * 063 * @see org.kuali.rice.kew.routemodule.FlexRMAdapter#findActionRequests(org.kuali.rice.kew.engine.RouteContext) 064 */ 065 @Override 066 public List<ActionRequestValue> findActionRequests(RouteContext context) throws Exception { 067 // comment this out while implementing the meta-rules stuff 068 // re-implement later 069 List<ActionRequestValue> actionRequests = new ArrayList<ActionRequestValue>(); 070 RouteNodeInstance currentNode = context.getNodeInstance(); 071 String contentFragment = currentNode.getRouteNode().getContentFragment(); 072 // parse with JDOM to reuse RuleXmlParser 073 Document doc = XmlHelper.trimSAXXml(new ByteArrayInputStream(contentFragment.getBytes())); 074 Element root = doc.getRootElement(); 075 List<String> ruleAttributeNames = new ArrayList<String>(); 076 List<String> ruleAttributeClassNames = new ArrayList<String>(); 077 List<String> xpathExpressions = new ArrayList<String>(); 078 // get the list of ruleAttributes to use 079 Element ruleAttributes = root.getChild("ruleAttributes"); 080 if (ruleAttributes != null) { 081 for (Object o : ruleAttributes.getChildren("name")) { 082 Element e = (Element) o; 083 ruleAttributeNames.add(e.getText()); 084 } 085 for (Object o : ruleAttributes.getChildren("className")) { 086 Element e = (Element) o; 087 ruleAttributeClassNames.add(e.getText()); 088 } 089 } 090 // get the list of xpath expressions to verify 091 for (Object o: root.getChildren("match")) { 092 Element e = (Element) o; 093 xpathExpressions.add(e.getText()); 094 } 095 if ( (ruleAttributeNames.isEmpty()) && (ruleAttributeClassNames.isEmpty()) && (xpathExpressions.isEmpty()) ) { 096 throw new RuntimeException("Match xpath expression not specified (should be parse-time exception...)"); 097 } 098 099 List<WorkflowRuleAttribute> attributes = new ArrayList<WorkflowRuleAttribute>(); 100 for (String attributeName : ruleAttributeNames) { 101 attributes.add(getRuleAttributeByName(attributeName)); 102 } 103 for (String attributeClassName : ruleAttributeClassNames) { 104 attributes.addAll(getRuleAttributeByClassName(attributeClassName)); 105 } 106 107 // at this point if we have no xpath expressions or attributes we cannot match 108 if (attributes.isEmpty() && xpathExpressions.isEmpty()) { 109 return actionRequests; 110 } 111 112 Boolean match = Boolean.TRUE; 113 if (!xpathExpressions.isEmpty()) { 114 XPath xpath = XPathHelper.newXPath(); 115 for (String xpathExpression : xpathExpressions) { 116 match &= (Boolean) xpath.evaluate(xpathExpression, context.getDocumentContent().getDocument(), XPathConstants.BOOLEAN); 117 } 118 } 119 for (WorkflowRuleAttribute workflowAttribute : attributes) { 120 // no rule extensions to pass in below because we have no rule... simple attribute matching only 121 match &= workflowAttribute.isMatch(context.getDocumentContent(), Collections.<RuleExtension>emptyList()); 122 } 123 124 if (match.booleanValue()) { 125 // LOG.debug("Expression '" + xpathExpression + "' matched document '" + context.getDocumentContent().getDocContent() + "'"); 126 } else { 127 // return an empty list because we didn't find a match using the given xpath 128 // LOG.debug("Expression '" + xpathExpression + "' did NOT match document '" + context.getDocumentContent().getDocContent() + "'"); 129 return actionRequests; 130 } 131 132 List<org.kuali.rice.kew.api.rule.RuleResponsibility> responsibilities = new ArrayList<org.kuali.rice.kew.api.rule.RuleResponsibility>(); 133 RuleXmlParser parser = new RuleXmlParser(); 134 ActionRequestFactory arf = new ActionRequestFactory(context.getDocument(), currentNode); 135 // this rule is only used to obtain description, forceAction flag, and the rulebasevalues id, which may be null 136 RuleBaseValues fakeRule = new RuleBaseValues(); 137 fakeRule.setName("fakeRule"); 138 fakeRule.setActive(Boolean.TRUE); 139 fakeRule.setCurrentInd(Boolean.TRUE); 140 fakeRule.setDescription("a fake rule"); 141 fakeRule.setForceAction(Boolean.TRUE); 142 fakeRule.setId(null); 143 144 for (Object o: root.getChildren("responsibility", XmlConstants.RULE_NAMESPACE)) { 145 Element e = (Element) o; 146 RuleResponsibilityBo responsibility = parser.parseResponsibility(e, fakeRule); 147 responsibility.setResponsibilityId(KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID); 148 responsibilities.add(org.kuali.rice.kew.api.rule.RuleResponsibility.Builder.create(responsibility).build()); 149 } 150 if (responsibilities.isEmpty()) { 151 throw new RuntimeException("No responsibilities found on node " + currentNode.getName()); 152 } 153 154 makeActionRequests(arf, responsibilities, context, RuleBaseValues.to(fakeRule), context.getDocument(), null, null); 155 actionRequests.addAll(arf.getRequestGraphs()); 156 return actionRequests; 157 } 158 159 @Override 160 public String toString() { 161 return "InlineRequestsRouteModule"; 162 } 163 164 private WorkflowRuleAttribute getRuleAttributeByName(String ruleAttributeName) { 165 return materializeRuleAttribute(KewApiServiceLocator.getExtensionRepositoryService().getExtensionByName( 166 ruleAttributeName)); 167 } 168 169 private List<WorkflowRuleAttribute> getRuleAttributeByClassName(String ruleAttributeClassName) { 170 List<ExtensionDefinition> extensionDefinitions = 171 KewApiServiceLocator.getExtensionRepositoryService().getExtensionsByResourceDescriptor(ruleAttributeClassName); 172 List<WorkflowRuleAttribute> workflowRuleAttributes = new ArrayList<WorkflowRuleAttribute>(); 173 for (ExtensionDefinition extension : extensionDefinitions) { 174 workflowRuleAttributes.add(materializeRuleAttribute(extension)); 175 } 176 return workflowRuleAttributes; 177 } 178 179 private WorkflowRuleAttribute materializeRuleAttribute(ExtensionDefinition extensionDefinition) { 180 if (extensionDefinition != null) { 181 if (KewApiConstants.RULE_ATTRIBUTE_TYPE.equals(extensionDefinition.getType())) { 182 ObjectDefinition objDef = new ObjectDefinition(extensionDefinition.getResourceDescriptor(), extensionDefinition.getApplicationId()); 183 return (WorkflowRuleAttribute) GlobalResourceLoader.getObject(objDef); 184 } else if (KewApiConstants.RULE_XML_ATTRIBUTE_TYPE.equals(extensionDefinition.getType())) { 185 ObjectDefinition objDef = new ObjectDefinition(extensionDefinition.getResourceDescriptor(), extensionDefinition.getApplicationId()); 186 WorkflowRuleAttribute workflowAttribute = (WorkflowRuleAttribute) GlobalResourceLoader.getObject(objDef); 187 //required to make it work because ruleAttribute XML is required to construct custom columns 188 ((GenericXMLRuleAttribute) workflowAttribute).setExtensionDefinition(extensionDefinition); 189 return workflowAttribute; 190 } 191 } 192 return null; 193 } 194 195 }