001 /**
002 * Copyright 2005-2012 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 }