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}