001/** 002 * Copyright 2005-2016 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 */ 016package org.kuali.rice.kew.impl.support.krms; 017 018import org.apache.commons.lang.StringUtils; 019import org.apache.log4j.Logger; 020import org.kuali.rice.core.api.config.ConfigurationException; 021import org.kuali.rice.core.api.reflect.ObjectDefinition; 022import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 023import org.kuali.rice.kew.actionrequest.ActionRequestValue; 024import org.kuali.rice.kew.api.exception.WorkflowException; 025import org.kuali.rice.kew.api.extension.ExtensionDefinition; 026import org.kuali.rice.kew.api.extension.ExtensionRepositoryService; 027import org.kuali.rice.kew.api.extension.ExtensionUtils; 028import org.kuali.rice.kew.engine.RouteContext; 029import org.kuali.rice.kew.engine.node.NodeState; 030import org.kuali.rice.kew.engine.node.RouteNode; 031import org.kuali.rice.kew.engine.node.RouteNodeUtils; 032import org.kuali.rice.kew.engine.node.service.RouteNodeService; 033import org.kuali.rice.kew.framework.support.krms.RulesEngineExecutor; 034import org.kuali.rice.kew.impl.peopleflow.PeopleFlowRouteModule; 035import org.kuali.rice.kew.routemodule.RouteModule; 036import org.kuali.rice.kew.util.ResponsibleParty; 037import org.kuali.rice.krms.api.KrmsApiServiceLocator; 038import org.kuali.rice.krms.api.engine.Engine; 039import org.kuali.rice.krms.api.engine.EngineResults; 040import org.w3c.dom.Element; 041 042import java.util.List; 043 044/** 045 * An implementation of a {@link RouteModule} which executes the KRMS rules engine using the configured 046 * {@link RulesEngineExecutor}. It then interprets those results and processes them, which may include instantiating 047 * and delegating to another RouteModule. Currently, this implementation only supports PeopleFlow results returned from 048 * KRMS and passes those off to the {@link org.kuali.rice.kew.impl.peopleflow.PeopleFlowRouteModule}. 049 * 050 * @author Kuali Rice Team (rice.collab@kuali.org) 051 */ 052public class RulesEngineRouteModule implements RouteModule { 053 054 private static final Logger LOG = Logger.getLogger(RulesEngineRouteModule.class); 055 056 private static final String RULES_ENGINE_ELEMENT = "rulesEngine"; 057 private static final String EXECUTOR_NAME_ATTRIBUTE = "executorName"; 058 private static final String EXECUTOR_CLASS_ATTRIBUTE = "executorClass"; 059 private static final String PEOPLE_FLOWS_SELECTED_ATTRIBUTE = "peopleFlowsSelected"; 060 061 private volatile Engine rulesEngine; 062 063 private PeopleFlowRouteModule peopleFlowRouteModule; 064 private ExtensionRepositoryService extensionRepositoryService; 065 private RouteNodeService routeNodeService; 066 067 @Override 068 public List<ActionRequestValue> findActionRequests(RouteContext context) throws Exception { 069 EngineResults engineResults = executeRulesEngine(context, getRulesEngine()); 070 if (engineResults != null) { 071 processEngineResults(context, engineResults); 072 } 073 return peopleFlowRouteModule.findActionRequests(context); 074 } 075 076 @Override 077 public ResponsibleParty resolveResponsibilityId(String responsibilityId) throws WorkflowException { 078 return null; 079 } 080 081 @Override 082 public boolean isMoreRequestsAvailable(RouteContext context) { 083 return peopleFlowRouteModule.isMoreRequestsAvailable(context); 084 } 085 086 protected EngineResults executeRulesEngine(RouteContext context, Engine rulesEngine) { 087 RulesEngineExecutor executor = loadRulesEngineExecutor(context); 088 return executor.execute(context, rulesEngine); 089 } 090 091 protected void processEngineResults(RouteContext context, EngineResults engineResults) { 092 String peopleFlowsSelected = (String)engineResults.getAttribute(PEOPLE_FLOWS_SELECTED_ATTRIBUTE); 093 if (StringUtils.isBlank(peopleFlowsSelected)) { 094 LOG.info("No PeopleFlows returned from KRMS execution."); 095 } else { 096 LOG.info("PeopleFlows returned from KRMS execution: " + peopleFlowsSelected); 097 } 098 NodeState nodeState = context.getNodeInstance().getNodeState(PeopleFlowRouteModule.PEOPLE_FLOW_SEQUENCE); 099 if (nodeState == null) { 100 nodeState = new NodeState(); 101 nodeState.setNodeInstance(context.getNodeInstance()); 102 nodeState.setKey(PeopleFlowRouteModule.PEOPLE_FLOW_SEQUENCE); 103 context.getNodeInstance().addNodeState(nodeState); 104 } 105 nodeState.setValue(peopleFlowsSelected); 106 if (!context.isSimulation()) { 107 routeNodeService.save(nodeState); 108 } 109 } 110 111 protected RulesEngineExecutor loadRulesEngineExecutor(RouteContext context) { 112 RouteNode routeNode = context.getNodeInstance().getRouteNode(); 113 Element rulesEngineElement = RouteNodeUtils.getCustomRouteNodeElement( 114 context.getNodeInstance().getRouteNode(), RULES_ENGINE_ELEMENT); 115 if (rulesEngineElement == null) { 116 throw new ConfigurationException("Failed to located rules engine configuration for route node: " + routeNode.getName()); 117 } 118 String executorName = rulesEngineElement.getAttribute(EXECUTOR_NAME_ATTRIBUTE); 119 String executorClassName = rulesEngineElement.getAttribute(EXECUTOR_CLASS_ATTRIBUTE); 120 if (StringUtils.isBlank(executorName) && StringUtils.isBlank(executorClassName)) { 121 throw new ConfigurationException("Failed to resolve a valid executor name or class name from rules engine configuration, was null or blank."); 122 } 123 RulesEngineExecutor rulesEngineExecutor = null; 124 if (StringUtils.isNotBlank(executorClassName)) { 125 rulesEngineExecutor = GlobalResourceLoader.getObject(new ObjectDefinition(executorClassName)); 126 } else if (StringUtils.isNotBlank(executorName)) { 127 ExtensionDefinition extensionDefinition = getExtensionRepositoryService().getExtensionByName(executorName); 128 if (extensionDefinition != null) { 129 rulesEngineExecutor = ExtensionUtils.loadExtension(extensionDefinition); 130 } 131 } 132 if (rulesEngineExecutor == null) { 133 throw new ConfigurationException("Failed to load RulesEngineExecutor for either executorName=" + executorName + " or executorClass=" + executorClassName); 134 } 135 return rulesEngineExecutor; 136 } 137 138 protected Engine getRulesEngine() { 139 if (rulesEngine == null) { 140 rulesEngine = KrmsApiServiceLocator.getEngine(); 141 } 142 return rulesEngine; 143 } 144 145 public PeopleFlowRouteModule getPeopleFlowRouteModule() { 146 return peopleFlowRouteModule; 147 } 148 149 public void setPeopleFlowRouteModule(PeopleFlowRouteModule peopleFlowRouteModule) { 150 this.peopleFlowRouteModule = peopleFlowRouteModule; 151 } 152 153 public ExtensionRepositoryService getExtensionRepositoryService() { 154 return extensionRepositoryService; 155 } 156 157 public void setExtensionRepositoryService(ExtensionRepositoryService extensionRepositoryService) { 158 this.extensionRepositoryService = extensionRepositoryService; 159 } 160 161 public RouteNodeService getRouteNodeService() { 162 return routeNodeService; 163 } 164 165 public void setRouteNodeService(RouteNodeService routeNodeService) { 166 this.routeNodeService = routeNodeService; 167 } 168 169}