/* ===============================================================================
*
* Part of the InfoGlue Content Management Platform (www.infoglue.org)
*
* ===============================================================================
*
* Copyright (C)
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2, as published by the
* Free Software Foundation. See the file LICENSE.html for more information.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc. / 59 Temple
* Place, Suite 330 / Boston, MA 02111-1307 / USA.
*
* ===============================================================================
*/
package org.infoglue.cms.util.workflow;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.cfg.Configuration;
import org.apache.log4j.Logger;
import org.infoglue.cms.entities.mydesktop.WorkflowActionVO;
import org.infoglue.cms.entities.mydesktop.WorkflowStepVO;
import org.infoglue.cms.entities.mydesktop.WorkflowVO;
import org.infoglue.cms.exception.SystemException;
import org.infoglue.cms.security.InfoGluePrincipal;
import org.infoglue.deliver.util.CacheController;
import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.workflow.AbstractWorkflow;
import com.opensymphony.workflow.InvalidActionException;
import com.opensymphony.workflow.StoreException;
import com.opensymphony.workflow.WorkflowException;
import com.opensymphony.workflow.config.DefaultConfiguration;
import com.opensymphony.workflow.loader.ActionDescriptor;
import com.opensymphony.workflow.loader.StepDescriptor;
import com.opensymphony.workflow.loader.WorkflowDescriptor;
import com.opensymphony.workflow.query.Expression;
import com.opensymphony.workflow.query.FieldExpression;
import com.opensymphony.workflow.query.NestedExpression;
import com.opensymphony.workflow.query.WorkflowExpressionQuery;
import com.opensymphony.workflow.spi.Step;
import com.opensymphony.workflow.spi.WorkflowEntry;
/**
* A facade to OSWorkflow that gives us a place to cache workflow data as we need it while interacting with it.
* This class has kind of a strange interface due to the idiosyncracies of the OSWorkflow, particularly
* the Workflow interface. The idea is to encapsulate the interactions with OSWorkflow and eliminate the
* need to pass a Workflow reference and the workflow ID all over the place when extracting data from OSWorkflow
* @author <a href="mailto:jedprentice@gmail.com">Jed Prentice</a>
* @version $Revision: 1.42 $ $Date: 2010/02/22 08:14:28 $
*/
public class WorkflowFacade
{
/**
* If the following attribute is specified in the workflow meta attributes,
* The title will be fetch from the propertyset associated with the workflow, using the meta value as a key.
*/
private static final String WORKFLOW_TITLE_EXTENSION_META_ATTRIBUTE = "org.infoglue.title";
/**
* If the following attribute is specified in the workflow meta attributes,
* then all actions will have access to a DatabaseSession instance controlled by this class.
*/
private static final String WORKFLOW_DATABASE_EXTENSION_META_ATTRIBUTE = "org.infoglue.database";
private final static Logger logger = Logger.getLogger(WorkflowFacade.class.getName());
private static SessionFactory hibernateSessionFactory;
static
{
try
{
hibernateSessionFactory = new Configuration().configure().buildSessionFactory();
}
catch (HibernateException e)
{
logger.error("An exception occurred when we tried to initialize the hibernateSessionFactory", e);
throw new ExceptionInInitializerError(e);
}
}
/**
* Keep track of the workflows that is currently executing.
*/
private static final Collection currentWorkflows = new ArrayList();
private final AbstractWorkflow workflow;
private long workflowId;
private WorkflowDescriptor workflowDescriptor;
/**
* Constructs a WorkflowFacade with the given owner.
*
* @param owner the owner of the workflow.
*/
public WorkflowFacade(final Owner owner)
{
this(owner, true);
}
/**
* Constructs a WorkflowFacade with the given owner.
*
* @param owner the owner of the workflow.
*/
public WorkflowFacade(final Owner owner, boolean createSession)
{
workflow = new InfoGlueBasicWorkflow(owner.getIdentifier());
workflow.getConfiguration().getPersistenceArgs().put("sessionFactory", hibernateSessionFactory);
if(createSession)
{
try
{
Session session = hibernateSessionFactory.openSession();
try
{
Map args = new HashMap();
args.put("sessionFactory", hibernateSessionFactory);
args.put("session", session);
workflow.getConfiguration().getWorkflowStore().init(args);
}
catch (StoreException e)
{
e.printStackTrace();
}
workflow.getConfiguration().getPersistenceArgs().put("session", session);
}
catch (HibernateException e)
{
e.printStackTrace();
}
}
}
/**
* Constructs a WorkflowFacade with the given owner.
*
* @param owner the owner of the workflow.
*/
public WorkflowFacade(final Owner owner, SessionFactory sessionFactory, Session session)
{
if(sessionFactory != null)
hibernateSessionFactory = sessionFactory;
workflow = new InfoGlueBasicWorkflow(owner.getIdentifier());
if(session != null)
{
com.opensymphony.workflow.config.Configuration config = new /*InfoGlueHibernate*/DefaultConfiguration();
config.getPersistenceArgs().put("session", session);
config.getPersistenceArgs().put("sessionFactory", hibernateSessionFactory);
workflow.setConfiguration(config);
}
else
{
workflow.getConfiguration().getPersistenceArgs().put("sessionFactory", hibernateSessionFactory);
}
}
/**
* Constructs a WorkflowFacade with the given user principal
* @param userPrincipal an InfoGluePrincipal representing a system user
*/
public WorkflowFacade(InfoGluePrincipal userPrincipal)
{
this(OwnerFactory.create(userPrincipal));
}
/**
* Constructs a WorkflowFacade with the given user principal
* @param userPrincipal an InfoGluePrincipal representing a system user
*/
public WorkflowFacade(InfoGluePrincipal userPrincipal, boolean createSession)
{
this(OwnerFactory.create(userPrincipal), createSession);
}
/**
* Constructs a WorkflowFacade with the given user principal
* @param userPrincipal an InfoGluePrincipal representing a system user
*/
public WorkflowFacade(InfoGluePrincipal userPrincipal, SessionFactory sessionFactory, Session session)
{
this(OwnerFactory.create(userPrincipal), sessionFactory, session);
}
/**
* Constructs a WorkflowFacade with the given user principal representing an initialized instance of the workflow
* with the given name. "Initialized" in this context means that the initial action has been executed and we have
* the workflow ID.
* @param userPrincipal an InfoGluePrincipal representing a system user
* @param name the name of the workflow to create
* @param initialAction the ID of the initial action to perform to get the workflow started.
*/
public WorkflowFacade(InfoGluePrincipal userPrincipal, String name, int initialAction) throws SystemException
{
this(userPrincipal, name, initialAction, new HashMap());
}
/**
* Constructs a WorkflowFacade with the given user principal representing an initialized instance of the workflow
* with the given name. "Initialized" in this context means that the initial action has been executed and we have
* the workflow ID.
* @param userPrincipal an InfoGluePrincipal representing a system user
* @param name the name of the workflow to create
* @param initialAction the ID of the initial action to perform to get the workflow started.
* @param inputs a map of inputs to use to initialize the workflow.
*/
public WorkflowFacade(InfoGluePrincipal userPrincipal, String name, int initialAction, Map inputs) throws SystemException
{
this(userPrincipal);
initialize(name, initialAction, inputs);
}
/**
* Constructs a WorkflowFacade with the given user principal representing an initialized instance of the workflow
* with the given name. "Initialized" in this context means that the initial action has been executed and we have
* the workflow ID.
* @param userPrincipal an InfoGluePrincipal representing a system user
* @param name the name of the workflow to create
* @param initialAction the ID of the initial action to perform to get the workflow started.
* @param inputs a map of inputs to use to initialize the workflow.
*/
public WorkflowFacade(InfoGluePrincipal userPrincipal, String name, int initialAction, Map inputs, SessionFactory sessionFactory, Session session) throws SystemException
{
this(userPrincipal, sessionFactory, session);
initialize(name, initialAction, inputs);
}
/**
* Constructs a WorkflowFacade for a user with the given workflow ID.
* @param userPrincipal an InfoGluePrincipal representing a system user
* @param workflowId the ID representing an instance of the desired workflow
*/
public WorkflowFacade(InfoGluePrincipal userPrincipal, long workflowId)
{
this(userPrincipal);
setWorkflowIdAndDescriptor(workflowId);
}
/**
* Constructs a WorkflowFacade for a user with the given workflow ID.
* @param userPrincipal an InfoGluePrincipal representing a system user
* @param workflowId the ID representing an instance of the desired workflow
*/
public WorkflowFacade(InfoGluePrincipal userPrincipal, long workflowId, boolean createSession)
{
this(userPrincipal, createSession);
setWorkflowIdAndDescriptor(workflowId);
}
/**
* Constructs a WorkflowFacade for a user with the given workflow ID.
* @param userPrincipal an InfoGluePrincipal representing a system user
* @param workflowId the ID representing an instance of the desired workflow
*/
public WorkflowFacade(InfoGluePrincipal userPrincipal, long workflowId, SessionFactory sessionFactory, Session session)
{
this(userPrincipal, sessionFactory, session);
setWorkflowIdAndDescriptor(workflowId);
}
/**
* Sets the workflow ID to the given value, and caches the associated workflow descriptor
* @param workflowId the desired workflow ID
*/
private void setWorkflowIdAndDescriptor(long workflowId)
{
this.workflowId = workflowId;
String key = "workflowName_" + workflowId;
String workflowName = (String)CacheController.getCachedObject("workflowNameCache", key);
if(workflowName == null)
{
workflowName = workflow.getWorkflowName(workflowId);
CacheController.cacheObject("workflowNameCache", key, workflowName);
}
String keyDescriptor = "workflowDescriptor_" + workflowId;
WorkflowDescriptor workflowDescriptorTemp = (WorkflowDescriptor)CacheController.getCachedObject("workflowNameCache", keyDescriptor);
if(workflowDescriptorTemp == null)
{
workflowDescriptorTemp = workflow.getWorkflowDescriptor(workflowName);
workflowDescriptor = workflowDescriptorTemp;
CacheController.cacheObject("workflowNameCache", keyDescriptor, workflowDescriptorTemp);
}
else
workflowDescriptor = workflowDescriptorTemp;
//workflowDescriptor = workflow.getWorkflowDescriptor(workflowName);
}
/**
* Returns the workflow ID
* @return the workflow ID
*/
public long getWorkflowId()
{
return workflowId;
}
/**
* Initializes the workflow, setting workflowId as a side-effect.
* @param name the name of the workflow to initialize
* @param initialAction the ID of the initial action to perform to get the workflow started.
* @param inputs a map of inputs to use to initialize the workflow.
* @throws SystemException if a workflow error occurs.
*/
private void initialize(String name, int initialAction, Map inputs) throws SystemException
{
try
{
if(useDatabaseExtension(workflow.getWorkflowDescriptor(name)))
{
setWorkflowIdAndDescriptor(doExtendedInitialize(name, initialAction, inputs));
}
else
{
setWorkflowIdAndDescriptor(workflow.initialize(name, initialAction, inputs));
}
}
catch (Exception e)
{
logger.error("An error occurred when we tried to get workflow with name:" + name);
throw new SystemException(e);
}
}
/**
* Initializes the workflow.
* A <code>DatabaseSession</code> object whose lifecycle is handled by this method is inserted into the <code>inputs</code>.
*
* @param name the name of the workflow to initialize
* @param initialAction the ID of the initial action to perform to get the workflow started.
* @param inputs a map of inputs to use to initialize the workflow.
* @throws SystemException if a workflow error occurs.
*/
private long doExtendedInitialize(final String name, final int initialAction, final Map inputs) throws WorkflowException
{
long result = 0;
final DatabaseSession db = new DatabaseSession();
try
{
final Map copy = new HashMap();
copy.putAll(inputs);
copy.put(workflow.getWorkflowDescriptor(name).getMetaAttributes().get(WORKFLOW_DATABASE_EXTENSION_META_ATTRIBUTE), db);
result = workflow.initialize(name, initialAction, copy);
}
catch(Exception e)
{
e.printStackTrace();
if(db != null)
{
db.setRollbackOnly();
}
throw (e instanceof WorkflowException) ? (WorkflowException) e : new WorkflowException(e);
}
finally
{
db.releaseDB();
}
return result;
}
/**
* Performs an action using the given inputs
* @param actionId the ID of the action to perform
* @param inputs a map of inputs to the action
* @throws WorkflowException if a workflow error occurs, or if the underlying workflow is not active
*/
public synchronized void doAction(int actionId, Map inputs) throws WorkflowException
{
if(logger.isInfoEnabled())
logger.info("doAction with " + actionId + " on " + this.workflowId);
try
{
final Long id = new Long(workflowId);
if(getEntryState() == WorkflowEntry.CREATED)
workflow.changeEntryState(workflowId, WorkflowEntry.ACTIVATED);
if(logger.isInfoEnabled())
{
logger.info("workflowId:" + workflowId);
logger.info("actionId:" + actionId);
}
synchronized(currentWorkflows)
{
if(!isActive())
{
if(getEntryState() == WorkflowEntry.UNKNOWN)
throw new WorkflowException("The workflow with id " + workflowId + " is in an unknown state - the database could be down or the workflow corrupt");
else
throw new InvalidActionException("Workflow " + workflowId + " is no longer active");
}
if(currentWorkflows.contains(id))
{
throw new WorkflowException("The selected workflow is executing...");
}
currentWorkflows.add(id);
}
try
{
if(useDatabaseExtension(workflowDescriptor))
{
doExtendedAction(actionId, inputs);
}
else
{
workflow.doAction(workflowId, actionId, inputs);
}
}
finally
{
synchronized(currentWorkflows)
{
currentWorkflows.remove(id);
}
}
}
catch(Exception we)
{
logger.error("An error occurred when we tried to invoke an workflow action:" + we.getMessage());
//restoreSessionFactory(workflow, we);
throw new WorkflowException("An error occurred when we tried to invoke an workflow action:" + we.getMessage());
}
}
/**
* Performs an action using the given inputs.
* A <code>DatabaseSession</code> object whose lifecycle is handled by this method is inserted into the <code>inputs</code>.
*
* @param actionId the ID of the action to perform
* @param inputs a map of inputs to the action
* @throws WorkflowException if a workflow error occurs, or if the underlying workflow is not active
*/
private void doExtendedAction(final int actionId, final Map inputs) throws WorkflowException
{
final DatabaseSession db = new DatabaseSession();
try
{
final Map copy = new HashMap();
copy.putAll(inputs);
copy.put(workflowDescriptor.getMetaAttributes().get(WORKFLOW_DATABASE_EXTENSION_META_ATTRIBUTE), db);
workflow.doAction(workflowId, actionId, copy);
}
catch(Exception e)
{
logger.error("An error occurred in doExtendedAction:" + e.getMessage(), e);
//e.printStackTrace();
if(db != null)
{
db.setRollbackOnly();
}
throw (e instanceof WorkflowException) ? (WorkflowException) e : new WorkflowException(e);
}
finally
{
db.releaseDB();
}
}
/**
* Returns the property set associated with the underlying workflow
* @return the property set associated with the underlying workflow
*/
public PropertySet getPropertySet()
{
//Timer t = new Timer();
String key = "psCache_" + workflowId;
PropertySet ps = (PropertySet)CacheController.getCachedObject("propertySetCache", key);
if(ps == null)
{
ps = workflow.getPropertySet(workflowId);
CacheController.cacheObject("propertySetCache", key, ps);
}
//t.printElapsedTime("getPropertySet took");
return ps;
}
/**
* Returns the state of the underlying workflow entry
* @return the state of the underlying workflow entry
*/
private int getEntryState() throws WorkflowException
{
try
{
return workflow.getEntryState(workflowId);
}
catch(Throwable we)
{
logger.error("An error occurred when we tried to check for entry state:" + we.getMessage());
//restoreSessionFactory(workflow, we);
throw new WorkflowException("An error occurred when we tried to check for entry state:" + we.getMessage());
}
}
/**
* Indicates whether the underlying workflow is active.
*
* @return true if the underlying workflow's state is WorkflowEntry.ACTIVATED, otherwise returns false.
*/
public boolean isActive() throws WorkflowException
{
return getEntryState() == WorkflowEntry.ACTIVATED;
}
/**
* Indicates whether the underlying workflow is finished.
*
* @return true if the underlying workflow's state is WorkflowEntry.KILLED or WorkflowEntry.COMPLETED, otherwise returns false.
*/
public boolean isFinished() throws WorkflowException
{
int state = getEntryState();
return state == WorkflowEntry.KILLED || state == WorkflowEntry.COMPLETED;
}
/**
* Returns a list of all declared workflows, i.e., workflows defined in workflows.xml
* @return a list WorkflowVOs representing all declared workflows
*/
public List<WorkflowVO> getDeclaredWorkflows()
{
String[] workflowNames = workflow.getWorkflowNames();
List<WorkflowVO> availableWorkflows = new ArrayList<WorkflowVO>();
for (int i = 0; i < workflowNames.length; i++)
{
try
{
availableWorkflows.add(createWorkflowVO(workflowNames[i]));
}
catch(Exception e)
{
logger.error("The workflow " + workflowNames[i] + " could not be instantiated:" + e.getMessage(), e);
}
}
return availableWorkflows;
}
/**
* Returns a list of all active workflows.
*
* @return a list of WorkflowVOs representing all active workflows
* @throws SystemException if an error occurs finding the active workflows
*/
public List<WorkflowVO> getActiveWorkflows(Integer maxNumberOfWorkflows) throws SystemException
{
List<WorkflowVO> workflowVOs = new ArrayList<WorkflowVO>();
List<WorkflowVO> activeWorkflows = findActiveWorkflows();
//logger.info("activeWorkflows:" + activeWorkflows.size());
if(maxNumberOfWorkflows != null && activeWorkflows.size() > maxNumberOfWorkflows)
activeWorkflows = activeWorkflows.subList(0, maxNumberOfWorkflows);
Iterator activeWorkflowsIterator = activeWorkflows.iterator();
while (activeWorkflowsIterator.hasNext())
{
setWorkflowIdAndDescriptor(((Long)activeWorkflowsIterator.next()).longValue());
//logger.info("workflowId:" + workflowId);
workflowVOs.add(createWorkflowVO());
}
return workflowVOs;
}
/**
* Returns a list of workflows owned by the specified principal. If the principal is
* an administrator, all active workflows are returned.
*
* @param principal the principal.
* @return the workflows owned by the specified principal.
*/
public List getMyActiveWorkflows(final InfoGluePrincipal principal, Integer maxNumberOfWorkflows) throws SystemException
{
String key = "myWorkflows_" + principal.getName();
List workflows = (List)CacheController.getCachedObject("myActiveWorkflows", key);
if(workflows == null)
{
if(principal.getIsAdministrator())
{
workflows = getActiveWorkflows(maxNumberOfWorkflows);
}
else
{
Collection owners = OwnerFactory.createAll(principal);
Expression[] expressions = new Expression[owners.size()];
Iterator ownersIterator = owners.iterator();
int i = 0;
while(ownersIterator.hasNext())
{
Owner owner = (Owner)ownersIterator.next();
Expression expression = new FieldExpression(FieldExpression.OWNER, FieldExpression.CURRENT_STEPS, FieldExpression.EQUALS, owner.getIdentifier());
expressions[i] = expression;
i++;
}
final Set workflowVOs = new HashSet();
workflowVOs.addAll(createWorkflowsForOwner(expressions, maxNumberOfWorkflows));
/*
final Set workflowVOs = new HashSet();
for(final Iterator owners = OwnerFactory.createAll(principal).iterator(); owners.hasNext(); )
{
final Owner owner = (Owner) owners.next();
workflowVOs.addAll(createWorkflowsForOwner(owner));
}
*/
workflows = new ArrayList(workflowVOs);
}
CacheController.cacheObject("myActiveWorkflows", key, workflows);
}
return workflows;
}
/**
* Creates value object for all workflows having the specified owner.
*
* @param owner the owner.
* @return the value objects.
* @throws SystemException if an error occurs when creating the value objects.
*/
private final Set createWorkflowsForOwner(final Owner owner) throws SystemException
{
final Set workflowVOs = new HashSet();
List workflows = findWorkflows(owner);
Iterator workflowsIterator = workflows.iterator();
while (workflowsIterator.hasNext())
{
setWorkflowIdAndDescriptor(((Long)workflowsIterator.next()).longValue());
workflowVOs.add(createWorkflowVO());
}
return workflowVOs;
}
/**
* Creates value object for all workflows having the specified owner.
*
* @param owner the owner.
* @return the value objects.
* @throws SystemException if an error occurs when creating the value objects.
*/
private final Set createWorkflowsForOwner(final Expression[] expressions, Integer maxNumberOfWorkflows) throws SystemException
{
try
{
final Set workflowVOs = new HashSet();
List workflows = findWorkflows(expressions);
if(maxNumberOfWorkflows != null && workflows.size() > maxNumberOfWorkflows)
workflows = workflows.subList(0, maxNumberOfWorkflows);
Iterator workflowsIterator = workflows.iterator();
while (workflowsIterator.hasNext())
{
setWorkflowIdAndDescriptor(((Long)workflowsIterator.next()).longValue());
workflowVOs.add(createWorkflowVO());
}
return workflowVOs;
}
catch (WorkflowException e)
{
throw new SystemException(e);
}
}
/**
* Finds all active workflows
* @return A list of workflowIds representing workflows that match the hard-wored query expression.
* @throws SystemException if a workflow error occurs during the search
*/
private List<WorkflowVO> findActiveWorkflows() throws SystemException
{
try
{
List<WorkflowVO> workflows = workflow.query(new WorkflowExpressionQuery(new FieldExpression(FieldExpression.STATE, FieldExpression.ENTRY, FieldExpression.EQUALS, new Integer(WorkflowEntry.ACTIVATED))));
return workflows;
//return workflow.query(new WorkflowExpressionQuery(new FieldExpression(FieldExpression.STATE, FieldExpression.ENTRY, FieldExpression.EQUALS, new Integer(WorkflowEntry.ACTIVATED))));
}
catch (WorkflowException e)
{
throw new SystemException(e);
}
}
/**
* Finds all workflows for the specified owner.
*
* @param owner the owner.
* @return The active workflows owned by the specified owner.
* @throws SystemException
*/
private List findWorkflows(final Owner owner) throws SystemException
{
try
{
List workflows = workflow.query(new WorkflowExpressionQuery(new FieldExpression(FieldExpression.OWNER,
FieldExpression.CURRENT_STEPS, FieldExpression.EQUALS, owner.getIdentifier())));
return workflows;
}
catch (WorkflowException e)
{
throw new SystemException(e);
}
}
/**
* Finds all workflows for the specified owner.
*
* @param owner the owner.
* @return The active workflows owned by the specified owner.
* @throws SystemException
*/
private List findWorkflows(final Expression[] expressions) throws WorkflowException
{
try
{
List workflows = workflow.query(new WorkflowExpressionQuery(new NestedExpression(expressions, NestedExpression.OR)));
//List workflows = workflow.query(new WorkflowExpressionQuery(new FieldExpression(FieldExpression.OWNER, FieldExpression.CURRENT_STEPS, FieldExpression.EQUALS, owner.getIdentifier())));
return workflows;
}
catch (WorkflowException we)
{
logger.error("An error occurred when we tried to invoke an workflow action:" + we.getMessage());
//restoreSessionFactory(workflow, we);
throw new WorkflowException("An error occurred when we tried to invoke an workflow action:" + we.getMessage());
}
}
/**
* Returns all current steps for the workflow, i.e., steps that could be performed in the workflow's current state
* Steps are filtered according to ownership; if a step has an owner, it is only included if the ownser matches
* the caller or if the current user is an administrator.
* @return a list of WorkflowStepVOs representing the current steps of the workflow with workflowId
*/
public List getCurrentSteps()
{
return getCurrentSteps(null);
}
public List getCurrentSteps(final WorkflowVO workflowVO)
{
return createStepVOs(workflowVO, workflow.getCurrentSteps(workflowId));
}
/**
* Returns all history steps for the workflow, i.e., all the steps that have already been performed.
* @return a list of WorkflowStepVOs representing all history steps for the workflow with workflowId
*/
public List getHistorySteps()
{
return getHistorySteps(null);
}
public List getHistorySteps(final WorkflowVO workflowVO)
{
return createStepVOs(workflowVO, workflow.getHistorySteps(workflowId));
}
/**
* Returns all steps for a workflow definition. These are the steps declared in the workfow descriptor; there is
* no knowledge of current or history steps at this point.
* @return a list of WorkflowStepVOs representing all steps in the workflow.
*/
public List getDeclaredSteps()
{
return getDeclaredSteps(workflowDescriptor);
}
/**
* Creates a list of WorkflowStepVOs from the given list of steps
* @param steps a list of Steps
* @return a list of WorkflowStepVOs corresponding to all steps that pass the filter
*/
private List createStepVOs(final WorkflowVO workflowVO, final List steps)
{
List stepVOs = new ArrayList();
for (Iterator i = steps.iterator(); i.hasNext();)
{
Step step = null;
step = (Step)i.next();
try
{
stepVOs.add(createStepVO(workflowVO, step));
}
catch(Exception e)
{
logger.warn("There was an invalid step:" + workflowVO, e);
}
}
return stepVOs;
}
/**
* Returns all steps for a workflow definition. These are the steps declared in the workfow descriptor; there is
* no knowledge of current or history steps at this point.
* @param descriptor a workflow descriptor from which to get current steps
* @return a list of WorkflowStepVOs representing all steps in the workflow.
*/
private List getDeclaredSteps(WorkflowDescriptor descriptor)
{
List steps = new ArrayList();
for (Iterator i = descriptor.getSteps().iterator(); i.hasNext();)
steps.add(createStepVO((StepDescriptor)i.next()));
return steps;
}
/**
* Returns a list of initial actions for the workflow
* @return a list of WorkflowActionVOs representing the global actions for the workflow with workflowId
*/
private List getInitialActions()
{
if(workflowDescriptor != null)
return createActionVOs(workflowDescriptor.getInitialActions());
else
return null;
}
/**
* Returns a list of global actions for the workflow
* @return a list of WorkflowActionVOs representing the global actions for the workflow with workflowId
*/
private List getGlobalActions()
{
if(workflowDescriptor != null)
return createActionVOs(workflowDescriptor.getGlobalActions());
else
return null;
}
/**
* Creates a list of WorkflowActionVOs from a list of action descriptors
* @param actionDescriptors a list of ActionDescriptors
* @return a list of WorkflowActionVOs representing actionDescriptors
*/
private List createActionVOs(List actionDescriptors)
{
List actions = new ArrayList();
for (Iterator i = actionDescriptors.iterator(); i.hasNext();)
actions.add(createActionVO((ActionDescriptor)i.next()));
return actions;
}
/**
* Creates a new WorkflowVO. This represents a pretty complete workflow; you get all the current steps, history
* steps, available actions, and global actions.
* @return a WorkflowVO representing workflow, with workflowId
*/
public WorkflowVO createWorkflowVO()
{
WorkflowVO workflowVO = new WorkflowVO(new Long(workflowId), workflow.getWorkflowName(workflowId));
if(workflowDescriptor != null)
{
if(useTitleExtension(workflowDescriptor))
workflowVO.setTitle(getWorkflowTitle());
}
workflowVO.setCurrentSteps(getCurrentSteps(workflowVO));
workflowVO.setHistorySteps(getHistorySteps(workflowVO));
workflowVO.setInitialActions(getInitialActions());
workflowVO.setGlobalActions(getGlobalActions());
return workflowVO;
}
/**
* Returns the title of the workflow instance.
*
* @return the title of the workflow instance.
*/
private String getWorkflowTitle()
{
if(!workflowDescriptor.getMetaAttributes().containsKey(WORKFLOW_TITLE_EXTENSION_META_ATTRIBUTE))
{
return null;
}
final String key = (String) workflowDescriptor.getMetaAttributes().get(WORKFLOW_TITLE_EXTENSION_META_ATTRIBUTE);
final PropertySet ps = getPropertySet();
return ps.exists(key) ? ps.getString(key) : null;
}
/**
* Creates a new WorkflowVO from workflow with the given name. The resulting workflow VO contains only a
* minimal amount of data because we don't have the workflow ID. Basically all you get is all the steps.
* @param name the name of the desired workflow
* @return a new WorkflowVO representing workflow
*/
private WorkflowVO createWorkflowVO(String name)
{
WorkflowVO workflowVO = new WorkflowVO(null, name);
try
{
workflowVO.setDeclaredSteps(getDeclaredSteps(workflow.getWorkflowDescriptor(name)));
}
catch(Exception e)
{
workflowVO.setStatus(WorkflowVO.STATUS_NOT_OK);
workflowVO.setStatusMessage("Error in workflow:" + e.getMessage());
logger.error("Could not read workflow:" + e.getMessage(), e);
}
return workflowVO;
}
/**
* Creates a WorkflowStepVO from the given step
* @param step the desired step
* @return a new WorkflowStepVO representing step.
*/
private WorkflowStepVO createStepVO(final WorkflowVO workflowVO, final Step step) throws Exception
{
logger.info("step:" + step + ':' + step.getId());
logger.info("Owner:" + step.getOwner());
WorkflowStepVO stepVO = new WorkflowStepVO(workflowVO);
stepVO.setId(new Integer((int)step.getId()));// Hope it doesn't get too big; we are stuck with int thanks to BaseEntityVO
stepVO.setStepId(new Integer(step.getStepId()));
stepVO.setWorkflowId(new Long(workflowId));
stepVO.setStatus(step.getStatus());
stepVO.setStartDate(step.getStartDate());
stepVO.setFinishDate(step.getFinishDate());
stepVO.setOwner(step.getOwner());
stepVO.setCaller(step.getCaller());
try
{
StepDescriptor stepDescriptor = workflowDescriptor.getStep(step.getStepId());
if(stepDescriptor != null)
{
stepVO.setName(stepDescriptor.getName());
for (Iterator i = stepDescriptor.getActions().iterator(); i.hasNext();)
stepVO.addAction(createActionVO((ActionDescriptor)i.next()));
}
else
{
throw new SystemException("No stepDescriptor found for " + step);
}
}
catch(Exception e)
{
}
return stepVO;
}
/**
* Creates a WorkflowStepVO from a step descriptor. Some of the step data, e.g., status, startDate,
* finishDate, etc. cannot be populated here because the step descriptor does not know about these things.
* @param stepDescriptor a step descriptor
* @return a WorkflowStepVO representing stepDescriptor
*/
private WorkflowStepVO createStepVO(StepDescriptor stepDescriptor)
{
WorkflowStepVO step = new WorkflowStepVO();
step.setStepId(new Integer(stepDescriptor.getId()));
step.setName(stepDescriptor.getName());
step.setStatus("Not started");
for (Iterator i = stepDescriptor.getActions().iterator(); i.hasNext();)
step.addAction(createActionVO((ActionDescriptor)i.next()));
return step;
}
/**
* Creates a WorkflowActionVO for the given action descriptor
* @param actionDescriptor an action descriptor
* @return a WorkflowActionVO representing actionDescriptor
*/
private WorkflowActionVO createActionVO(ActionDescriptor actionDescriptor)
{
logger.info("Action:" + actionDescriptor.getId() + ':' + actionDescriptor.getName()
+ ':' + actionDescriptor.getParent().getClass());
WorkflowActionVO actionVO = new WorkflowActionVO(new Integer(actionDescriptor.getId()));
actionVO.setWorkflowId(new Long(workflowId));
actionVO.setName(actionDescriptor.getName());
actionVO.setView(actionDescriptor.getView());
actionVO.setAutoExecute(actionDescriptor.getAutoExecute());
actionVO.setMetaAttributes(actionDescriptor.getMetaAttributes());
return actionVO;
}
/**
* Checks if the title extension should be used.
*
* @return true if the extension should be used, false otherwise.
*/
private boolean useTitleExtension(final WorkflowDescriptor descriptor)
{
return descriptor.getMetaAttributes().containsKey(WORKFLOW_TITLE_EXTENSION_META_ATTRIBUTE);
}
/**
* Checks if the database extension should be used.
*
* @return true if the extension should be used, false otherwise.
*/
private boolean useDatabaseExtension(final WorkflowDescriptor descriptor)
{
return descriptor.getMetaAttributes().containsKey(WORKFLOW_DATABASE_EXTENSION_META_ATTRIBUTE);
}
}