001 /** 002 * Copyright 2005-2013 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.impl.peopleflow; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.kuali.rice.core.api.config.ConfigurationException; 020 import org.kuali.rice.core.api.exception.RiceRuntimeException; 021 import org.kuali.rice.core.api.util.xml.XmlJotter; 022 import org.kuali.rice.kew.actionrequest.ActionRequestValue; 023 import org.kuali.rice.kew.api.action.ActionRequestType; 024 import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition; 025 import org.kuali.rice.kew.api.peopleflow.PeopleFlowService; 026 import org.kuali.rice.kew.engine.RouteContext; 027 import org.kuali.rice.kew.engine.node.NodeState; 028 import org.kuali.rice.kew.engine.node.RouteNodeUtils; 029 import org.kuali.rice.kew.api.exception.WorkflowException; 030 import org.kuali.rice.kew.routemodule.RouteModule; 031 import org.kuali.rice.kew.service.KEWServiceLocator; 032 import org.kuali.rice.kew.util.ResponsibleParty; 033 import org.w3c.dom.Element; 034 035 import javax.xml.bind.JAXBContext; 036 import javax.xml.bind.JAXBException; 037 import javax.xml.bind.annotation.XmlAttribute; 038 import javax.xml.bind.annotation.XmlElement; 039 import javax.xml.bind.annotation.XmlElements; 040 import javax.xml.bind.annotation.XmlRootElement; 041 import javax.xml.bind.annotation.XmlValue; 042 import java.util.ArrayList; 043 import java.util.List; 044 045 public class PeopleFlowRouteModule implements RouteModule { 046 047 private static final String PEOPLE_FLOW_PROPERTY = "peopleFlow"; 048 049 private static final String PEOPLE_FLOW_ITERATION_KEY = "peopleFlowIteration"; 050 public static final String PEOPLE_FLOW_SEQUENCE = "peopleFlowSequence"; 051 052 private static final JAXBContext jaxbContext; 053 static { 054 try { 055 jaxbContext = JAXBContext.newInstance(PeopleFlowConfig.class); 056 } catch (JAXBException e) { 057 throw new RiceRuntimeException("Failed to initialize JAXB!", e); 058 } 059 } 060 061 private PeopleFlowService peopleFlowService; 062 private PeopleFlowRequestGenerator peopleFlowRequestGenerator; 063 064 @Override 065 public List<ActionRequestValue> findActionRequests(RouteContext context) throws Exception { 066 int iteration = incrementIteration(context); 067 List<PeopleFlowConfig> configurations = parsePeopleFlowConfiguration(context); 068 List<ActionRequestValue> actionRequests = new ArrayList<ActionRequestValue>(); 069 int index = 0; 070 for (PeopleFlowConfig configuration : configurations) { 071 if (index++ == iteration) { 072 PeopleFlowDefinition peopleFlow = loadPeopleFlow(configuration); 073 actionRequests.addAll(getPeopleFlowRequestGenerator().generateRequests(context, peopleFlow, configuration.actionRequested)); 074 break; 075 } 076 } 077 return actionRequests; 078 } 079 080 @Override 081 public boolean isMoreRequestsAvailable(RouteContext context) { 082 Integer currentIteration = getCurrentIteration(context); 083 if (currentIteration == null) { 084 return false; 085 } 086 List<PeopleFlowConfig> configurations = parsePeopleFlowConfiguration(context); 087 return currentIteration.intValue() < configurations.size(); 088 } 089 090 @Override 091 public ResponsibleParty resolveResponsibilityId(String responsibilityId) throws WorkflowException { 092 return null; 093 } 094 095 protected List<PeopleFlowConfig> parsePeopleFlowConfiguration(RouteContext context) { 096 List<PeopleFlowConfig> configs = new ArrayList<PeopleFlowConfig>(); 097 NodeState peopleFlowSequenceNodeState = context.getNodeInstance().getNodeState(PEOPLE_FLOW_SEQUENCE); 098 if (peopleFlowSequenceNodeState != null) { 099 String peopleFlowSequence = peopleFlowSequenceNodeState.getValue(); 100 if (StringUtils.isNotBlank(peopleFlowSequence)) { 101 String[] peopleFlowValues = peopleFlowSequence.split(","); 102 for (String peopleFlowValue : peopleFlowValues) { 103 String[] peopleFlowProperties = peopleFlowValue.split(":"); 104 PeopleFlowConfig config = new PeopleFlowConfig(); 105 config.actionRequested = ActionRequestType.fromCode(peopleFlowProperties[0]); 106 config.peopleFlowIdentifier = peopleFlowProperties[1]; 107 configs.add(config); 108 } 109 } 110 } else { 111 List<Element> peopleFlowElements = RouteNodeUtils.getCustomRouteNodeElements( 112 context.getNodeInstance().getRouteNode(), PEOPLE_FLOW_PROPERTY); 113 114 for (Element peopleFlowElement : peopleFlowElements) { 115 try { 116 PeopleFlowConfig config = (PeopleFlowConfig)jaxbContext.createUnmarshaller().unmarshal(peopleFlowElement); 117 if (config.actionRequested == null) { 118 // default action request type to approve 119 config.actionRequested = ActionRequestType.APPROVE; 120 } 121 if (config == null) { 122 throw new IllegalStateException("People flow configuration element did not properly unmarshall from XML: " + XmlJotter.jotNode(peopleFlowElement)); 123 } 124 configs.add(config); 125 } catch (JAXBException e) { 126 throw new RiceRuntimeException("Failed to unmarshall people flow configuration from route node.", e); 127 } 128 } 129 } 130 return configs; 131 } 132 133 protected PeopleFlowDefinition loadPeopleFlow(PeopleFlowConfig configuration) { 134 if (configuration.isId()) { 135 PeopleFlowDefinition peopleFlow = getPeopleFlowService().getPeopleFlow(configuration.getId()); 136 if (peopleFlow == null) { 137 throw new ConfigurationException("Failed to locate a people flow with the given id of '" + configuration.getId() + "'"); 138 } 139 return peopleFlow; 140 } else { 141 String namespaceCode = configuration.getName().namespaceCode; 142 String name = configuration.getName().name; 143 PeopleFlowDefinition peopleFlow = getPeopleFlowService().getPeopleFlowByName(namespaceCode, name); 144 if (peopleFlow == null) { 145 throw new ConfigurationException("Failed to locate a people flow with the given namespaceCode of '" + namespaceCode + "' and name of '" + name + "'"); 146 } 147 return peopleFlow; 148 } 149 } 150 151 protected int incrementIteration(RouteContext context) { 152 int nextIteration = 0; 153 NodeState nodeState = context.getNodeInstance().getNodeState(PEOPLE_FLOW_ITERATION_KEY); 154 if (nodeState == null) { 155 nodeState = new NodeState(); 156 nodeState.setNodeInstance(context.getNodeInstance()); 157 nodeState.setKey(PEOPLE_FLOW_ITERATION_KEY); 158 context.getNodeInstance().addNodeState(nodeState); 159 } else { 160 int currentIteration = Integer.parseInt(nodeState.getValue()); 161 nextIteration = currentIteration + 1; 162 } 163 nodeState.setValue(Integer.toString(nextIteration)); 164 if (!context.isSimulation()) { 165 KEWServiceLocator.getRouteNodeService().save(nodeState); 166 } 167 return nextIteration; 168 } 169 170 protected Integer getCurrentIteration(RouteContext context) { 171 NodeState nodeState = context.getNodeInstance().getNodeState(PEOPLE_FLOW_ITERATION_KEY); 172 if (nodeState == null) { 173 return null; 174 } 175 return Integer.valueOf(nodeState.getValue()); 176 } 177 178 public PeopleFlowService getPeopleFlowService() { 179 return peopleFlowService; 180 } 181 182 public void setPeopleFlowService(PeopleFlowService peopleFlowService) { 183 this.peopleFlowService = peopleFlowService; 184 } 185 186 public PeopleFlowRequestGenerator getPeopleFlowRequestGenerator() { 187 return peopleFlowRequestGenerator; 188 } 189 190 public void setPeopleFlowRequestGenerator(PeopleFlowRequestGenerator peopleFlowRequestGenerator) { 191 this.peopleFlowRequestGenerator = peopleFlowRequestGenerator; 192 } 193 194 @XmlRootElement(name = "peopleFlow") 195 private static class PeopleFlowConfig { 196 197 @XmlElement(name = "actionRequested") 198 ActionRequestType actionRequested; 199 200 @XmlElements(value = { 201 @XmlElement(name = "name", type = NameConfig.class), 202 @XmlElement(name = "id", type = String.class) 203 }) 204 Object peopleFlowIdentifier; 205 206 boolean isId() { 207 return peopleFlowIdentifier instanceof String; 208 } 209 boolean isName() { 210 return peopleFlowIdentifier instanceof NameConfig; 211 } 212 213 NameConfig getName() { 214 return (NameConfig)peopleFlowIdentifier; 215 } 216 217 String getId() { 218 return (String)peopleFlowIdentifier; 219 } 220 221 } 222 223 private static class NameConfig { 224 225 @XmlAttribute(name = "namespace") 226 String namespaceCode; 227 228 @XmlValue 229 String name; 230 231 } 232 233 }