/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.xmlworkflow;
import org.apache.log4j.Logger;
import org.apache.xpath.XPathAPI;
import org.dspace.content.Collection;
import org.dspace.core.ConfigurationManager;
import org.dspace.utils.DSpace;
import org.dspace.xmlworkflow.state.Step;
import org.dspace.xmlworkflow.state.actions.UserSelectionActionConfig;
import org.dspace.xmlworkflow.state.Workflow;
import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerException;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* The workflowfactory is responsible for parsing the
* workflow xml file and is used to retrieve the workflow for
* a certain collection
*
* @author Bram De Schouwer (bram.deschouwer at dot com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class WorkflowFactory {
private static Logger log = Logger.getLogger(WorkflowFactory.class);
private static HashMap<String, Workflow> workflowCache;
private static String path = ConfigurationManager.getProperty("dspace.dir")+"/config/workflow.xml";
// private static String pathActions = ConfigurationManager.getProperty("dspace.dir")+"/config/workflow-actions.xml";
public static Workflow getWorkflow(Collection collection) throws IOException, WorkflowConfigurationException {
//Initialize our cache if we have none
if(workflowCache == null)
workflowCache = new HashMap<String, Workflow>();
// Attempt to retrieve our workflow object
if(workflowCache.get(collection.getHandle())==null){
try{
// No workflow cache found for the collection, check if we have a workflowId for this collection
File xmlFile = new File(path);
Document input = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile);
Node mainNode = input.getFirstChild();
Node workflowMap = XPathAPI.selectSingleNode(mainNode, "//workflow-map/name-map[@collection='"+collection.getHandle()+"']");
if(workflowMap==null){
//No workflowId found for this collection, so retrieve & use the default workflow
if(workflowCache.get("default")==null){
String workflowID = XPathAPI.selectSingleNode(mainNode, "//workflow-map/name-map[@collection='default']").getAttributes().getNamedItem("workflow").getTextContent();
if(workflowID==null){
throw new WorkflowConfigurationException("No mapping is present for collection with handle:" + collection.getHandle());
}
Node workflowNode = XPathAPI.selectSingleNode(mainNode, "//workflow[@id='"+workflowID+"']");
Workflow wf = new Workflow(workflowID, getRoles(workflowNode));
Step step = createFirstStep(wf, workflowNode);
wf.setFirstStep(step);
workflowCache.put("default", wf);
workflowCache.put(collection.getHandle(), wf);
return wf;
}else{
return workflowCache.get("default");
}
}else{
//We have a workflowID so retrieve it & resolve it to a workflow, also store it in our cache
String workflowID = workflowMap.getAttributes().getNamedItem("workflow").getTextContent();
Node workflowNode = XPathAPI.selectSingleNode(mainNode, "//workflow[@id='"+workflowID+"']");
Workflow wf = new Workflow(workflowID, getRoles(workflowNode));
Step step = createFirstStep(wf, workflowNode);
wf.setFirstStep(step);
workflowCache.put(collection.getHandle(), wf);
return wf;
}
} catch (Exception e){
log.error("Error while retrieving workflow for collection: " + collection.getHandle(), e);
throw new WorkflowConfigurationException("Error while retrieving workflow for the following collection: " + collection.getHandle());
}
}else{
return workflowCache.get(collection.getHandle());
}
}
private static Step createFirstStep(Workflow workflow, Node workflowNode) throws TransformerException, WorkflowConfigurationException {
String firstStepID = workflowNode.getAttributes().getNamedItem("start").getTextContent();
Node stepNode = XPathAPI.selectSingleNode(workflowNode, "//step[@id='"+firstStepID+"']");
if(stepNode == null){
throw new WorkflowConfigurationException("First step does not exist for workflow: "+workflowNode.getAttributes().getNamedItem("id").getTextContent());
}
Node roleNode = stepNode.getAttributes().getNamedItem("role");
Role role = null;
if(roleNode != null)
role = workflow.getRoles().get(roleNode.getTextContent());
String userSelectionActionID = stepNode.getAttributes().getNamedItem("userSelectionMethod").getTextContent();
UserSelectionActionConfig userSelection = createUserAssignmentActionConfig(userSelectionActionID);
return new Step(firstStepID, workflow, role, userSelection, getStepActionConfigs(stepNode), getStepOutcomes(stepNode), getNbRequiredUser(stepNode));
}
private static Map<Integer, String> getStepOutcomes(Node stepNode) throws TransformerException, WorkflowConfigurationException {
try{
NodeList outcomesNodeList = XPathAPI.selectNodeList(stepNode, "outcomes/step");
Map<Integer, String> outcomes = new HashMap<Integer, String>();
//Add our outcome, should it be null it will be interpreted as the end of the line (last step)
for(int i = 0; i < outcomesNodeList.getLength(); i++){
Node outcomeNode = outcomesNodeList.item(i);
int index = Integer.parseInt(outcomeNode.getAttributes().getNamedItem("status").getTextContent());
if(index < 0){
throw new WorkflowConfigurationException("Outcome configuration error for step: "+stepNode.getAttributes().getNamedItem("id").getTextContent());
}
outcomes.put(index, outcomeNode.getTextContent());
}
return outcomes;
}catch(Exception e){
log.error("Outcome configuration error for step: " + stepNode.getAttributes().getNamedItem("id").getTextContent(), e);
throw new WorkflowConfigurationException("Outcome configuration error for step: "+stepNode.getAttributes().getNamedItem("id").getTextContent());
}
}
private static int getNbRequiredUser(Node stepnode){
if(stepnode.getAttributes().getNamedItem("requiredUsers")!=null){
return Integer.parseInt(stepnode.getAttributes().getNamedItem("requiredUsers").getTextContent());
}
return 1;
}
private static List<String> getStepActionConfigs(Node stepNode) throws TransformerException {
NodeList actionConfigNodes = XPathAPI.selectNodeList(stepNode, "actions/action");
List<String> actionConfigIDs = new ArrayList<String>();
for(int i = 0; i < actionConfigNodes.getLength(); i++){
actionConfigIDs.add(actionConfigNodes.item(i).getAttributes().getNamedItem("id").getTextContent());
}
return actionConfigIDs;
}
public static Step createStep(Workflow workflow, String stepID) throws WorkflowConfigurationException, IOException {
try{
File xmlFile = new File(path);
Document input = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile);
Node mainNode = input.getFirstChild();
Node stepNode = XPathAPI.selectSingleNode(mainNode, "//workflow[@id='"+workflow.getID()+"']/step[@id='"+stepID+"']");
if(stepNode == null){
throw new WorkflowConfigurationException("Step does not exist for workflow: "+workflow.getID());
}
Node roleNode = stepNode.getAttributes().getNamedItem("role");
Role role = null;
if(roleNode != null)
role = workflow.getRoles().get(roleNode.getTextContent());
String userSelectionActionID = stepNode.getAttributes().getNamedItem("userSelectionMethod").getTextContent();
UserSelectionActionConfig userSelection = createUserAssignmentActionConfig(userSelectionActionID);
return new Step(stepID, workflow, role, userSelection, getStepActionConfigs(stepNode), getStepOutcomes(stepNode), getNbRequiredUser(stepNode));
}catch (Exception e){
log.error("Error while creating step with :" + stepID, e);
throw new WorkflowConfigurationException("Step: " + stepID + " does not exist for workflow: "+workflow.getID());
}
}
private static UserSelectionActionConfig createUserAssignmentActionConfig(String userSelectionActionID) {
return new DSpace().getServiceManager().getServiceByName(userSelectionActionID, UserSelectionActionConfig.class);
}
public static WorkflowActionConfig createWorkflowActionConfig(String actionID){
return new DSpace().getServiceManager().getServiceByName(actionID, WorkflowActionConfig.class);
}
private static LinkedHashMap<String, Role> getRoles(Node workflowNode) throws WorkflowConfigurationException {
NodeList roleNodes = null;
try{
roleNodes = XPathAPI.selectNodeList(workflowNode, "roles/role");
}catch (Exception e){
log.error("Error while resolving nodes", e);
throw new WorkflowConfigurationException("Error while retrieving roles");
}
LinkedHashMap<String, Role> roles = new LinkedHashMap<String, Role>();
for(int i = 0; i < roleNodes.getLength(); i++){
String roleID = roleNodes.item(i).getAttributes().getNamedItem("id").getTextContent();
String roleName = roleNodes.item(i).getAttributes().getNamedItem("name").getTextContent();
Node descriptionNode = roleNodes.item(i).getAttributes().getNamedItem("description");
String roleDescription = null;
if(descriptionNode != null)
roleDescription = descriptionNode.getTextContent();
Node scopeNode = roleNodes.item(i).getAttributes().getNamedItem("scope");
String roleScope = null;
if(scopeNode != null)
roleScope = scopeNode.getTextContent();
Node internalNode = roleNodes.item(i).getAttributes().getNamedItem("internal");
String roleInternal;
boolean internal = false;
if(internalNode != null){
roleInternal = internalNode.getTextContent();
internal = Boolean.parseBoolean(roleInternal);
}
Role.Scope scope;
if(roleScope == null || roleScope.equalsIgnoreCase("collection"))
scope = Role.Scope.COLLECTION;
else
if(roleScope.equalsIgnoreCase("item"))
scope = Role.Scope.ITEM;
else
if(roleScope.equalsIgnoreCase("repository"))
scope = Role.Scope.REPOSITORY;
else
throw new WorkflowConfigurationException("An invalid role scope has been specified it must either be item or collection.");
Role role = new Role(roleID, roleName, roleDescription,internal, scope);
roles.put(roleID, role);
}
return roles;
}
}