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.peopleflow;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.config.ConfigurationException;
020import org.kuali.rice.core.api.exception.RiceRuntimeException;
021import org.kuali.rice.core.api.util.xml.XmlJotter;
022import org.kuali.rice.kew.actionrequest.ActionRequestValue;
023import org.kuali.rice.kew.api.action.ActionRequestType;
024import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
025import org.kuali.rice.kew.api.peopleflow.PeopleFlowService;
026import org.kuali.rice.kew.engine.RouteContext;
027import org.kuali.rice.kew.engine.node.NodeState;
028import org.kuali.rice.kew.engine.node.RouteNodeUtils;
029import org.kuali.rice.kew.api.exception.WorkflowException;
030import org.kuali.rice.kew.routemodule.RouteModule;
031import org.kuali.rice.kew.service.KEWServiceLocator;
032import org.kuali.rice.kew.util.ResponsibleParty;
033import org.w3c.dom.Element;
034
035import javax.xml.bind.JAXBContext;
036import javax.xml.bind.JAXBException;
037import javax.xml.bind.annotation.XmlAttribute;
038import javax.xml.bind.annotation.XmlElement;
039import javax.xml.bind.annotation.XmlElements;
040import javax.xml.bind.annotation.XmlRootElement;
041import javax.xml.bind.annotation.XmlValue;
042import java.util.ArrayList;
043import java.util.List;
044
045public 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}