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    }