View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.impl.peopleflow;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.config.ConfigurationException;
20  import org.kuali.rice.core.api.exception.RiceRuntimeException;
21  import org.kuali.rice.core.api.util.xml.XmlJotter;
22  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
23  import org.kuali.rice.kew.api.action.ActionRequestType;
24  import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
25  import org.kuali.rice.kew.api.peopleflow.PeopleFlowService;
26  import org.kuali.rice.kew.engine.RouteContext;
27  import org.kuali.rice.kew.engine.node.NodeState;
28  import org.kuali.rice.kew.engine.node.RouteNodeUtils;
29  import org.kuali.rice.kew.api.exception.WorkflowException;
30  import org.kuali.rice.kew.routemodule.RouteModule;
31  import org.kuali.rice.kew.service.KEWServiceLocator;
32  import org.kuali.rice.kew.util.ResponsibleParty;
33  import org.w3c.dom.Element;
34  
35  import javax.xml.bind.JAXBContext;
36  import javax.xml.bind.JAXBException;
37  import javax.xml.bind.annotation.XmlAttribute;
38  import javax.xml.bind.annotation.XmlElement;
39  import javax.xml.bind.annotation.XmlElements;
40  import javax.xml.bind.annotation.XmlRootElement;
41  import javax.xml.bind.annotation.XmlValue;
42  import java.util.ArrayList;
43  import java.util.List;
44  
45  public class PeopleFlowRouteModule implements RouteModule {
46  
47      private static final String PEOPLE_FLOW_PROPERTY = "peopleFlow";
48  
49      private static final String PEOPLE_FLOW_ITERATION_KEY = "peopleFlowIteration";
50      public static final String PEOPLE_FLOW_SEQUENCE = "peopleFlowSequence";
51  
52      private static final JAXBContext jaxbContext;
53      static {
54          try {
55              jaxbContext = JAXBContext.newInstance(PeopleFlowConfig.class);
56          } catch (JAXBException e) {
57              throw new RiceRuntimeException("Failed to initialize JAXB!", e);
58          }
59      }
60  
61      private PeopleFlowService peopleFlowService;
62      private PeopleFlowRequestGenerator peopleFlowRequestGenerator;
63  
64      @Override
65      public List<ActionRequestValue> findActionRequests(RouteContext context) throws Exception {
66          int iteration = incrementIteration(context);
67          List<PeopleFlowConfig> configurations = parsePeopleFlowConfiguration(context);
68          List<ActionRequestValue> actionRequests = new ArrayList<ActionRequestValue>();
69          int index = 0;
70          for (PeopleFlowConfig configuration : configurations) {
71              if (index++ == iteration) {
72                  PeopleFlowDefinition peopleFlow = loadPeopleFlow(configuration);
73                  actionRequests.addAll(getPeopleFlowRequestGenerator().generateRequests(context, peopleFlow, configuration.actionRequested));
74                  break;
75              }
76          }
77          return actionRequests;
78      }
79  
80      @Override
81      public boolean isMoreRequestsAvailable(RouteContext context) {
82          Integer currentIteration = getCurrentIteration(context);
83          if (currentIteration == null) {
84              return false;
85          }
86          List<PeopleFlowConfig> configurations = parsePeopleFlowConfiguration(context);
87          return currentIteration.intValue() < configurations.size();
88      }
89  
90      @Override
91      public ResponsibleParty resolveResponsibilityId(String responsibilityId) throws WorkflowException {
92          return null;
93      }
94  
95      protected List<PeopleFlowConfig> parsePeopleFlowConfiguration(RouteContext context) {
96          List<PeopleFlowConfig> configs = new ArrayList<PeopleFlowConfig>();
97          NodeState peopleFlowSequenceNodeState = context.getNodeInstance().getNodeState(PEOPLE_FLOW_SEQUENCE);
98          if (peopleFlowSequenceNodeState != null) {
99              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 }