//----------------------------BEGIN LICENSE----------------------------
/*
* Willow : the Open Source WorkFlow Project
* Distributable under GNU LGPL license by gun.org
*
* Copyright (C) 2004-2010 huihoo.org
* Copyright (C) 2004-2010 ZosaTapo <dertyang@hotmail.com>
*
* ====================================================================
* Project Homepage : http://www.huihoo.org/willow
* Source Forge : http://sourceforge.net/projects/huihoo
* Mailing list : willow@lists.sourceforge.net
*/
//----------------------------END LICENSE-----------------------------
package org.huihoo.workflow.impl.store.util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.workflow.rules.ScriptException;
import org.huihoo.willow.core.WorkflowServiceBase;
import org.huihoo.workflow.WorkflowException;
import org.huihoo.workflow.store.SchemaContext;
import org.huihoo.workflow.store.persistent.UserTransaction;
import org.huihoo.workflow.store.spi.SpiCaseDatabase;
import org.huihoo.workflow.runtime.WorkflowCase;
import org.huihoo.workflow.runtime.WorkflowContext;
import org.huihoo.workflow.runtime.WorkflowService;
import org.huihoo.workflow.runtime.WorkflowSubWork;
import org.huihoo.workflow.runtime.WorkflowWork;
import org.huihoo.workflow.usermodel.WorkflowParticipant;
import org.huihoo.workflow.xpdl.WorkflowActivity;
import org.huihoo.workflow.xpdl.WorkflowPackage;
import org.huihoo.workflow.xpdl.WorkflowProcess;
import org.huihoo.workflow.xpdl.WorkflowTransition;
import org.huihoo.workflow.xpdl.activity.Implementation;
import org.huihoo.workflow.xpdl.activity.JoinType;
import org.huihoo.workflow.xpdl.activity.PerformerType;
import org.huihoo.workflow.xpdl.activity.SubFlowImplementation;
import org.huihoo.workflow.xpdl.event.WorkflowActivityEvent;
import org.huihoo.workflow.xpdl.event.WorkflowActivityListener;
import com.zosatapo.commons.store.ConnUtils;
/**
* @author reic
*
* To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
public class DispatchController
{
private static Log log = LogFactory.getLog(DispatchController.class);
public static boolean accept_core(
WorkflowService workflowService,
WorkflowProcess workflowProcess,
WorkflowParticipant operator,
SpiCaseDatabase caseDatabase,
UserTransaction userTransaction,
WorkflowWork workflowWork)
throws WorkflowException
{
log.debug("[accept_core] workitem: " + workflowWork.getInfo());
WorkflowActivity activity = workflowWork.getWorkflowActivity();
WorkflowActivityEvent event = new WorkflowActivityEvent(workflowWork, WorkflowActivityEvent.WORK_ACCEPT);
WorkflowActivityListener[] listeners = activity.getWorkflowActivityListeners();
for (int i = 0; i < listeners.length; ++i)
{
listeners[i].beforeAccepted(event);
}
workflowWork.setAcceptTime(new java.util.Date());
caseDatabase.updateWorkflowWork(workflowProcess, operator, userTransaction, workflowWork);
Implementation impl = activity.getImplementation();
WorkflowServiceBase context=(WorkflowServiceBase)workflowService;
ClassLoader loader=context.getLoader().getClassLoader();
impl.execute(workflowWork,loader);
for (int i = 0; i < listeners.length; ++i)
{
listeners[i].afterAccepted(event);
}
return true;
}
public static boolean dispatch_core(
WorkflowService workflowService,
WorkflowProcess workflowProcess,
WorkflowParticipant operator,
SpiCaseDatabase caseDatabase,
UserTransaction userTransaction,
WorkflowWork workflowWork)
throws WorkflowException, ScriptException
{
log.debug("[dispatch_core] workitem: " + workflowWork.getInfo());
WorkflowActivity workflowActivity = workflowWork.getWorkflowActivity();
WorkflowActivityEvent event = new WorkflowActivityEvent(workflowWork, WorkflowActivityEvent.WORK_DISPATCH);
WorkflowActivityListener[] listeners = workflowActivity.getWorkflowActivityListeners();
for (int i = 0; i < listeners.length; ++i)
{
listeners[i].beforeDispatched(event);
}
WorkflowCase workflowCase = workflowWork.getWorkflowCase();
WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();
workflowWork.setDispatchTime(new java.util.Date());
caseDatabase.updateWorkflowWork(workflowProcess, operator, userTransaction, workflowWork);
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
WorkflowTransition customizedTransition = dispatch_revert(workflowService,workflowProcess,operator,caseDatabase,userTransaction,workflowWork);
List filterTrans = null;
if (customizedTransition == null)
{
filterTrans =
OutTransitionFilter.filterOutTransition(workflowService,
operator,
caseDatabase,
userTransaction,
workflowWork);
}
else
{
filterTrans = new ArrayList();
filterTrans.add(customizedTransition);
}
int sizeTrans = filterTrans.size();
if (sizeTrans > 0)
{
WorkflowTransition transition = null;
//-------------------------------------------------------------
// �������ɷ������Ĺ�����ʹ����ͬ���ɷ��Ǻ�
//--------------------------------------------------------------
String pathDispBatch = null;
pathDispBatch =
caseDatabase.getCaseIdGenerator().genWorkflowPathBatch(
workflowPackage.getUUID(),
workflowProcess.getUUID(),
workflowActivity.getUUID(),
workflowCase.getUUID());
ParallelThread pThreads[] =new ParallelThread[sizeTrans];
for (int i = 0; i < sizeTrans; ++i)
{
transition = (WorkflowTransition) filterTrans.get(i);
// dispatch_control(workflowService,
// workflowProcess,
// operator,
// caseDatabase,
// userTransaction,
// workflowWork,
// transition,
// pathDispBatch);
pThreads[i]=new ParallelThread(workflowService,
workflowProcess,
operator,
caseDatabase,
userTransaction,
workflowWork,
transition,
pathDispBatch);
pThreads[i].start();
}
WorkflowException dispatch_exception = null;
for (int i = 0; i < sizeTrans; ++i)
{
try
{
pThreads[i].join();
if(pThreads[i].getException() != null)
{
dispatch_exception = pThreads[i].getException();
}
}
catch(InterruptedException ex)
{
dispatch_exception = new WorkflowException("another thread has interrupted the ParallelThread");
}
}
if(dispatch_exception != null)
{
throw dispatch_exception;
}
}
else
{
// how I can do? just log it
WorkflowContext context = workflowService.getWorkflowContext();
String message =
"---------BEGIN-------------Damn It ,Fatal Error----------------------\n"
+ "no available outgoing transition found. shXt, willow is powered by zosatapo (dertyang@hotmail.com) . \n"
+ "\t\t PackageID = "
+ workflowPackage.getUUID()
+ "\n"
+ "\t\t ProcessID = "
+ workflowProcess.getUUID()
+ "\n"
+ "\t\t ActivityID = "
+ workflowActivity.getUUID()
+ "\n"
+ "---------END-------------Damn It ,Fatal Error----------------------\n";
context.log(message);
}
for (int i = 0; i < listeners.length; ++i)
{
listeners[i].afterDispatched(event);
}
return true;
}
public static boolean revert_core(
WorkflowService workflowService,
WorkflowProcess workflowProcess,
WorkflowParticipant operator,
SpiCaseDatabase caseDatabase,
UserTransaction userTransaction,
WorkflowWork workflowWork)
throws WorkflowException
{
log.debug("[revert_core] workitem: " + workflowWork.getInfo());
WorkflowActivity activity = workflowWork.getWorkflowActivity();
WorkflowActivityEvent event = new WorkflowActivityEvent(workflowWork, WorkflowActivityEvent.WORK_REVERT);
WorkflowActivityListener[] listeners = activity.getWorkflowActivityListeners();
for (int i = 0; i < listeners.length; ++i)
{
listeners[i].beforeReverted(event);
}
if (!JoinType.JOIN_NA.equals(activity.getJoinType()))
{
throw new WorkflowException("WorkflowActivity JoinType not supported");
}
String strSQL =
"SELECT vc_from,vc_batchNo FROM "
+ caseDatabase.getSchemaContext().getTableName(SchemaContext.SCHEMA_PATH)
+ " "
+ "WHERE vc_packageid=? "
+ "AND vc_processid=? "
+ "AND vc_caseid=? "
+ "AND vc_to=? ";
Connection conn = (Connection) userTransaction.getStoreConnection().get();
PreparedStatement pstmt = null;
WorkflowCase workflowCase = workflowWork.getWorkflowCase();
WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();
String fromWorkID = null;
String batchNo = null;
try
{
pstmt = conn.prepareStatement(strSQL);
pstmt.setString(1, workflowPackage.getUUID());
pstmt.setString(2, workflowProcess.getUUID());
pstmt.setString(3, workflowCase.getUUID());
pstmt.setString(4, workflowWork.getUUID());
ResultSet rs = pstmt.executeQuery();
if (rs.next())
{
fromWorkID = rs.getString("vc_from");
batchNo = rs.getString("vc_batchNo");
rs.close();
pstmt.close();
strSQL =
"SELECT count(*) FROM "
+ caseDatabase.getSchemaContext().getTableName(SchemaContext.SCHEMA_PATH)
+ " "
+ "WHERE vc_packageid=? "
+ "AND vc_processid=? "
+ "AND vc_caseid=? "
+ "AND vc_from=? "
+ "AND vc_batchNo=? ";
pstmt = conn.prepareStatement(strSQL);
pstmt.setString(1, workflowPackage.getUUID());
pstmt.setString(2, workflowProcess.getUUID());
pstmt.setString(3, workflowCase.getUUID());
pstmt.setString(4, fromWorkID);
pstmt.setString(5, batchNo);
rs = pstmt.executeQuery();
if (rs.next())
{
int count = rs.getInt(1);
if (count > 1)
{
//ǰ�̻�ַ���Ψһ,���ܽ��л���
throw new WorkflowException("WorkflowWork broadcast dispatch");
}
}
else
{
//never to reach here
throw new WorkflowException("WorkflowPath not found");
}
}
else
{
throw new WorkflowException("WorkflowPath not found ");
}
}
catch (SQLException ex)
{
throw new WorkflowException(ex);
}
finally
{
ConnUtils.cleanupNoThrow(pstmt);
}
WorkflowWork fromWork =
caseDatabase.findWorkflowWork(
workflowProcess,
operator,
userTransaction,
workflowCase,
fromWorkID);
WorkflowParticipant participant = fromWork.getPerformer();
String dispWorkId =
caseDatabase.createWorkflowWork(
workflowProcess,
operator,
userTransaction,
workflowCase,
activity,
participant,
workflowWork.getDispatchTime());
String pathDispBatch =
caseDatabase.getCaseIdGenerator().genWorkflowPathBatch(
workflowPackage.getUUID(),
workflowProcess.getUUID(),
activity.getUUID(),
workflowCase.getUUID());
WorkflowWork dispWork =
caseDatabase.findWorkflowWork(workflowProcess, operator, workflowCase, dispWorkId);
caseDatabase.createWorkflowPath(
workflowProcess,
operator,
userTransaction,
workflowCase,
workflowWork,
dispWork,
pathDispBatch,
WorkflowActivityEvent.WORK_REVERT);
for (int i = 0; i < listeners.length; ++i)
{
listeners[i].afterReverted(event);
}
return true;
}
public static boolean fetch_core(
WorkflowService workflowService,
WorkflowProcess workflowProcess,
WorkflowParticipant operator,
SpiCaseDatabase caseDatabase,
UserTransaction userTransaction,
WorkflowWork workflowWork)
throws WorkflowException
{
log.debug("[fetch_core] workitem: " + workflowWork.getInfo());
WorkflowActivity activity = workflowWork.getWorkflowActivity();
WorkflowActivityEvent event = new WorkflowActivityEvent(workflowWork, WorkflowActivityEvent.WORK_FETCH);
WorkflowActivityListener[] listeners = activity.getWorkflowActivityListeners();
for (int i = 0; i < listeners.length; ++i)
{
listeners[i].beforeFetched(event);
}
String strSQL =
"SELECT vc_to FROM "
+ caseDatabase.getSchemaContext().getTableName(SchemaContext.SCHEMA_PATH)
+ " "
+ "WHERE vc_packageid=? "
+ "AND vc_processid=? "
+ "AND vc_caseid=? "
+ "AND vc_from=? ";
Connection conn = (Connection) userTransaction.getStoreConnection().get();
PreparedStatement pstmt = null;
WorkflowCase workflowCase = workflowWork.getWorkflowCase();
WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();
try
{
pstmt = conn.prepareStatement(strSQL);
pstmt.setString(1, workflowPackage.getUUID());
pstmt.setString(2, workflowProcess.getUUID());
pstmt.setString(3, workflowCase.getUUID());
pstmt.setString(4, workflowWork.getUUID());
ResultSet rs = pstmt.executeQuery();
if (rs.next())
{
String toWorkID = rs.getString("vc_to");
if (rs.next())
{
//��·�ַ���֧��ȡ�ز���
throw new WorkflowException("WorkflowWork broadcast dispatch");
}
WorkflowWork toWork =
caseDatabase.findWorkflowWork(
workflowProcess,
operator,
userTransaction,
workflowCase,
rs.getString("vc_to"));
caseDatabase.deleteWorkflowWork(workflowProcess, operator, userTransaction, toWork);
workflowWork.setDispatchTime(null);
caseDatabase.updateWorkflowWork(workflowProcess, operator, userTransaction, workflowWork);
}
else
{
//never to reach here
throw new WorkflowException("WorkflowPath not found ");
}
}
catch (SQLException ex)
{
throw new WorkflowException(ex);
}
finally
{
ConnUtils.cleanupNoThrow(pstmt);
}
for (int i = 0; i < listeners.length; ++i)
{
listeners[i].atferFetched(event);
}
return true;
}
private static WorkflowTransition dispatch_revert(
WorkflowService workflowService,
WorkflowProcess workflowProcess,
WorkflowParticipant operator,
SpiCaseDatabase caseDatabase,
UserTransaction userTransaction,
WorkflowWork workflowWork)
throws WorkflowException
{
WorkflowActivity workflowActivity = workflowWork.getWorkflowActivity();
WorkflowCase workflowCase = workflowWork.getWorkflowCase();
WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();
WorkflowTransition rever_transition = null;
//�ɷ��Ĺ��������ɻ���(revert)�����Ĺ������Ҫ�����
String strSQL =
"SELECT vc_from,vc_owner_processid,vc_transitionid FROM "
+ caseDatabase.getSchemaContext().getTableName(SchemaContext.SCHEMA_PATH)
+ " "
+ "WHERE vc_packageid=? "
+ "AND vc_processid=? "
+ "AND vc_caseid=? "
+ "AND vc_to=? "
+ "AND int_eventType=? ";
Connection conn = (Connection) userTransaction.getStoreConnection().get();
PreparedStatement pstmt = null;
try
{
pstmt = conn.prepareStatement(strSQL);
pstmt.setString(1, workflowPackage.getUUID());
pstmt.setString(2, workflowProcess.getUUID());
pstmt.setString(3, workflowCase.getUUID());
pstmt.setString(4, workflowWork.getUUID());
pstmt.setInt(5, WorkflowActivityEvent.WORK_REVERT);//<-----very important
ResultSet rs = pstmt.executeQuery();
if (rs.next())
{
String fromWorkID = rs.getString("vc_from");
//----------------------------------------------------------------------------
// ֧�ֻ��˴���Ĺ���������ڵ���Ҫ�����ϸ��������
//
// ���Ի��˴���Ĺ�����ǰ�̹��������Ψһ��
// ͬʱǰ�̹������ֻ��Ψһ�ĺ�̹����� (���ϵ�����,���ݽṹ)
//----------------------------------------------------------------------------
if (rs.next())
{
throw new WorkflowException("Fatal WorkflowService Engine Error");
}
WorkflowWork fromWork =
caseDatabase.findWorkflowWork(
workflowProcess,
operator,
userTransaction,
workflowCase,
fromWorkID);
String vc_owner_processid = rs.getString("vc_owner_processid");
if (vc_owner_processid != null && !"".equals(vc_owner_processid))
{
String vc_transitionid = rs.getString("vc_transitionid");
WorkflowProcess owner_processid = workflowPackage.findWorkflowProcess(vc_owner_processid);
rever_transition = owner_processid.findWorkflowTransition(vc_transitionid);
}
else
{
rever_transition =
workflowProcess.findWorkflowTransition(
workflowWork.getWorkflowActivity(),
fromWork.getWorkflowActivity());
}
//�����ߴ������������ڲ����ݽṹ�����й�ϵ
workflowCase.getCaseContext().setPerformersByTransition(
rever_transition,
new WorkflowParticipant[] { fromWork.getPerformer()});
}
}
catch (SQLException ex)
{
throw new WorkflowException(ex);
}
finally
{
ConnUtils.cleanupNoThrow(pstmt);
}
return rever_transition;
}
private static boolean dispatch_control(
WorkflowService workflowService,
WorkflowProcess workflowProcess,
WorkflowParticipant operator,
SpiCaseDatabase caseDatabase,
UserTransaction userTransaction,
WorkflowWork workflowWork,
WorkflowTransition workflowTransition,
String pathDispBatch)
throws WorkflowException, ScriptException
{
log.debug(
"[dispatch_control] workitem: " + workflowWork.getInfo() + " | transition: " + workflowTransition.getInfo());
WorkflowActivity fromActivity=workflowWork.getWorkflowActivity();
WorkflowActivity toActivity = workflowTransition.getToWorkflowActivity();
Implementation impl = toActivity.getImplementation();
if (impl instanceof SubFlowImplementation)
{
//-----------------------------------------------------------
// ���������̴�����
//-----------------------------------------------------------
return DefaultActivityController.dispatch_subflow(workflowService,
workflowProcess,
operator,
caseDatabase,
userTransaction,
workflowWork,
workflowTransition,
pathDispBatch);
}
WorkflowActivity ownerActivity = workflowWork.getWorkflowActivity();
WorkflowProcess ownerProcess = ownerActivity.getWorkflowProcess();
WorkflowActivity endActivity = ownerProcess.getEndActivity();
if (endActivity.getUUID().equals(toActivity.getUUID()))
{
if (workflowWork instanceof WorkflowSubWork)
{
return DefaultActivityController.dispatch_r2p(workflowService,
workflowProcess,
operator,
caseDatabase,
userTransaction,
workflowWork,
workflowTransition,
pathDispBatch);
}
else
{
return dispatch_endflow(workflowService,
workflowProcess,
operator,
caseDatabase,
userTransaction,
workflowWork,
workflowTransition,
pathDispBatch);
}
}
else
{
JoinType joinType = toActivity.getJoinType();
if (joinType!=null && !JoinType.JOIN_NA.equals(joinType))
{
return JoinActivityController.dispatch_join(workflowService,
workflowProcess,
operator,
caseDatabase,
userTransaction,
workflowWork,
workflowTransition,
pathDispBatch);
}
else
{
return DefaultActivityController.dispatch_general(workflowService,
workflowProcess,
operator,
caseDatabase,
userTransaction,
workflowWork,
workflowTransition,
pathDispBatch);
}
}
}
public static boolean execute_dispatchIfNecessary(
WorkflowService workflowService,
WorkflowProcess workflowProcess,
WorkflowParticipant operator,
SpiCaseDatabase caseDatabase,
UserTransaction userTransaction,
WorkflowWork workflowWork)
throws WorkflowException, ScriptException
{
log.debug("[execute_dispatchIfNecessary] workitem: " + workflowWork.getInfo());
WorkflowActivity workActivity = workflowWork.getWorkflowActivity();
Implementation impl = workActivity.getImplementation();
if (impl instanceof SubFlowImplementation)
{
return accept_core(workflowService,workflowProcess, operator, caseDatabase, userTransaction, workflowWork)
&& dispatch_core(workflowService,workflowProcess, operator, caseDatabase, userTransaction, workflowWork);
}
PerformerType performerType = workActivity.getPerformerType();
if (PerformerType.PERFORMER_AUTOMATION.equals(performerType))
{
return accept_core(workflowService,workflowProcess, operator, caseDatabase, userTransaction, workflowWork)
&& dispatch_core(workflowService,workflowProcess, operator, caseDatabase, userTransaction, workflowWork);
}
return true;
}
//end the org.huihoo.workflow process
public static boolean dispatch_endflow(
WorkflowService workflowService,
WorkflowProcess workflowProcess,
WorkflowParticipant operator,
SpiCaseDatabase caseDatabase,
UserTransaction userTransaction,
WorkflowWork workflowWork,
WorkflowTransition workflowTransition,
String pathDispBatch)
throws WorkflowException
{
log.debug(
"[dispatch_endflow] workitem: " + workflowWork.getInfo() + " | transition: " + workflowTransition.getInfo());
WorkflowCase workflowCase = workflowWork.getWorkflowCase();
caseDatabase.createWorkflowPath(
workflowProcess,
operator,
userTransaction,
workflowCase,
workflowWork,
null,
pathDispBatch,
WorkflowActivityEvent.WORK_DISPATCH);
caseDatabase.closeWorkflowCase(workflowProcess, operator, userTransaction, workflowCase);
return true;
}
//---------------------------------------------------------------------------
//parallel execution thread class
//---------------------------------------------------------------------------
static class ParallelThread extends Thread
{
//==========================================================
private WorkflowService workflowService;
private WorkflowProcess workflowProcess;
private WorkflowParticipant operator;
private SpiCaseDatabase caseDatabase;
private UserTransaction userTransaction;
private WorkflowWork workflowWork;
private WorkflowTransition workflowTransition;
private String pathDispBatch;
//==========================================================
private WorkflowException run_exception;
//==========================================================
public ParallelThread(WorkflowService workflowService,
WorkflowProcess workflowProcess,
WorkflowParticipant operator,
SpiCaseDatabase caseDatabase,
UserTransaction userTransaction,
WorkflowWork workflowWork,
WorkflowTransition workflowTransition,
String pathDispBatch)
{
this.workflowService = workflowService;
this.workflowProcess = workflowProcess;
this.operator = operator;
this.caseDatabase = caseDatabase;
this.userTransaction = userTransaction;
this.workflowWork = workflowWork;
this.workflowTransition = workflowTransition;
this.pathDispBatch = pathDispBatch;
}
public void run()
{
try
{
dispatch_control(workflowService,
workflowProcess,
operator,
caseDatabase,
userTransaction,
workflowWork,
workflowTransition,
pathDispBatch);
}
catch(WorkflowException ex)
{
// WorkflowContext context = workflowService.getWorkflowContext();
// context.log("ParallelThread invoke dispatch_control",ex);
run_exception = ex;
}
}
public WorkflowException getException()
{
return run_exception;
}
}
}