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 processEngineResults(context, engineResults); 071 return peopleFlowRouteModule.findActionRequests(context); 072 } 073 074 @Override 075 public ResponsibleParty resolveResponsibilityId(String responsibilityId) throws WorkflowException { 076 return null; 077 } 078 079 @Override 080 public boolean isMoreRequestsAvailable(RouteContext context) { 081 return peopleFlowRouteModule.isMoreRequestsAvailable(context); 082 } 083 084 protected EngineResults executeRulesEngine(RouteContext context, Engine rulesEngine) { 085 RulesEngineExecutor executor = loadRulesEngineExecutor(context); 086 return executor.execute(context, rulesEngine); 087 } 088 089 protected void processEngineResults(RouteContext context, EngineResults engineResults) { 090 String peopleFlowsSelected = (String)engineResults.getAttribute(PEOPLE_FLOWS_SELECTED_ATTRIBUTE); 091 if (StringUtils.isBlank(peopleFlowsSelected)) { 092 LOG.info("No PeopleFlows returned from KRMS execution."); 093 } else { 094 LOG.info("PeopleFlows returned from KRMS execution: " + peopleFlowsSelected); 095 } 096 NodeState nodeState = context.getNodeInstance().getNodeState(PeopleFlowRouteModule.PEOPLE_FLOW_SEQUENCE); 097 if (nodeState == null) { 098 nodeState = new NodeState(); 099 nodeState.setNodeInstance(context.getNodeInstance()); 100 nodeState.setKey(PeopleFlowRouteModule.PEOPLE_FLOW_SEQUENCE); 101 context.getNodeInstance().addNodeState(nodeState); 102 } 103 nodeState.setValue(peopleFlowsSelected); 104 if (!context.isSimulation()) { 105 routeNodeService.save(nodeState); 106 } 107 } 108 109 protected RulesEngineExecutor loadRulesEngineExecutor(RouteContext context) { 110 RouteNode routeNode = context.getNodeInstance().getRouteNode(); 111 Element rulesEngineElement = RouteNodeUtils.getCustomRouteNodeElement( 112 context.getNodeInstance().getRouteNode(), RULES_ENGINE_ELEMENT); 113 if (rulesEngineElement == null) { 114 throw new ConfigurationException("Failed to located rules engine configuration for route node: " + routeNode.getName()); 115 } 116 String executorName = rulesEngineElement.getAttribute(EXECUTOR_NAME_ATTRIBUTE); 117 String executorClassName = rulesEngineElement.getAttribute(EXECUTOR_CLASS_ATTRIBUTE); 118 if (StringUtils.isBlank(executorName) && StringUtils.isBlank(executorClassName)) { 119 throw new ConfigurationException("Failed to resolve a valid executor name or class name from rules engine configuration, was null or blank."); 120 } 121 RulesEngineExecutor rulesEngineExecutor = null; 122 if (StringUtils.isNotBlank(executorClassName)) { 123 rulesEngineExecutor = GlobalResourceLoader.getObject(new ObjectDefinition(executorClassName)); 124 } else if (StringUtils.isNotBlank(executorName)) { 125 ExtensionDefinition extensionDefinition = getExtensionRepositoryService().getExtensionByName(executorName); 126 if (extensionDefinition != null) { 127 rulesEngineExecutor = ExtensionUtils.loadExtension(extensionDefinition); 128 } 129 } 130 if (rulesEngineExecutor == null) { 131 throw new ConfigurationException("Failed to load RulesEngineExecutor for either executorName=" + executorName + " or executorClass=" + executorClassName); 132 } 133 return rulesEngineExecutor; 134 } 135 136 protected Engine getRulesEngine() { 137 if (rulesEngine == null) { 138 rulesEngine = KrmsApiServiceLocator.getEngine(); 139 } 140 return rulesEngine; 141 } 142 143 public PeopleFlowRouteModule getPeopleFlowRouteModule() { 144 return peopleFlowRouteModule; 145 } 146 147 public void setPeopleFlowRouteModule(PeopleFlowRouteModule peopleFlowRouteModule) { 148 this.peopleFlowRouteModule = peopleFlowRouteModule; 149 } 150 151 public ExtensionRepositoryService getExtensionRepositoryService() { 152 return extensionRepositoryService; 153 } 154 155 public void setExtensionRepositoryService(ExtensionRepositoryService extensionRepositoryService) { 156 this.extensionRepositoryService = extensionRepositoryService; 157 } 158 159 public RouteNodeService getRouteNodeService() { 160 return routeNodeService; 161 } 162 163 public void setRouteNodeService(RouteNodeService routeNodeService) { 164 this.routeNodeService = routeNodeService; 165 } 166 167}