//----------------------------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.coreImpl.store;

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 java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.commons.jdbc.JDBCUtilities;
import org.huihoo.workflow.SchemaContext;
import org.huihoo.workflow.WorkflowException;
import org.huihoo.workflow.WorkflowTransaction;
import org.huihoo.workflow.WorkflowUtilities;
import org.huihoo.workflow.coreImpl.util.StringManager;
import org.huihoo.workflow.event.WorkEvent;
import org.huihoo.workflow.server.WorkflowCase;
import org.huihoo.workflow.server.WorkflowContext;
import org.huihoo.workflow.server.WorkflowPath;
import org.huihoo.workflow.server.WorkflowSession;
import org.huihoo.workflow.server.WorkflowWork;
import org.huihoo.workflow.server.WorkflowXPDL;
import org.huihoo.workflow.store.ArbitraryDispatchException;
import org.huihoo.workflow.store.ArbitraryFetchException;
import org.huihoo.workflow.store.Constants;
import org.huihoo.workflow.store.WorkflowCaseDatabase;
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;

public class ControllerDispatcher
{
	private static Log log = LogFactory.getLog("willow.ControllerDispatcher");

	/**
	 * The string manager for this package.
	 */
	private static StringManager sm = StringManager.getManager(org.huihoo.workflow.coreImpl.store.Constants.PACKAGE);

	public static boolean accept_core(
		WorkflowSession workflowSession,
		WorkflowXPDL workflowTemplate,
		WorkflowTransaction userTransaction,
		WorkflowProcess workflowProcess,
		WorkflowWork workflowWork)
		throws WorkflowException
	{
		WorkflowActivity activity = workflowWork.getActivity();

		if (workflowWork.getAcceptTime() != null)
		{
			throw new WorkflowException(sm.getString("work.alreadyAccepted"));
		}

		workflowWork.setAcceptTime(new java.util.Date());

		WorkflowCaseDatabase caseDatabase = workflowTemplate.getWorkflowTemplate().getCaseDatabase();

		try
		{
			caseDatabase.updateWork(workflowSession, userTransaction, workflowProcess, workflowWork,"");

			Implementation impl = activity.getImplementation();
			impl.execute(workflowWork, Thread.currentThread().getContextClassLoader());
		}
		catch (WorkflowException ex)
		{
			workflowWork.setAcceptTime(null);
			throw ex;
		}

		return true;
	}

	public static boolean dispatch_core(
		WorkflowSession workflowSession,
		WorkflowXPDL workflowTemplate,
		WorkflowTransaction userTransaction,
		WorkflowProcess workflowProcess,
		WorkflowWork workflowWork,
		Map userObject)
		throws WorkflowException
	{
		int disptachStyle = Constants.getDispatchStyle(userObject);
		Constants.setDispatchStyle(userObject, Constants.SUBMIT);

		switch (disptachStyle)
		{
			case Constants.RESUBMIT :
				{
					return ControllerDispatcher.submit_core(
						workflowSession,
						workflowTemplate,
						userTransaction,
						workflowProcess,
						workflowWork,
						userObject,
						true);
				}

			case Constants.BACKTRACK :
				{
					return ControllerDispatcher.backtrack_core(
						workflowSession,
						workflowTemplate,
						userTransaction,
						workflowProcess,
						workflowWork,
						userObject);
				}

			case Constants.REVERSE :
				{
					return ControllerDispatcher.reverse_core(
						workflowSession,
						workflowTemplate,
						userTransaction,
						workflowProcess,
						workflowWork,
						userObject);
				}

			default :
				{
				}
				break;
		}

		return ControllerDispatcher.submit_core(
			workflowSession,
			workflowTemplate,
			userTransaction,
			workflowProcess,
			workflowWork,
			userObject,
			false);
	}

	public static boolean fetch_core(
		WorkflowSession workflowSession,
		WorkflowXPDL workflowTemplate,
		WorkflowTransaction userTransaction,
		WorkflowProcess workflowProcess,
		WorkflowWork workflowWork)
		throws WorkflowException
	{
		WorkflowCaseDatabase caseDatabase = workflowTemplate.getWorkflowTemplate().getCaseDatabase();

		WorkflowActivity activity = workflowWork.getActivity();

		String strSQL =
			"SELECT vc_uuid,vc_to FROM "
				+ userTransaction.getDatabase().getSchemaContext().getTableName(SchemaContext.SCHEMA_PATH)
				+ "  "
				+ "WHERE  vc_packageid=? "
				+ "AND    vc_processid=? "
				+ "AND    vc_caseid=?  "
				+ "AND    vc_from=? ";

		Connection conn = userTransaction.getConnection();
		PreparedStatement pstmt = null;

		WorkflowCase workflowCase = workflowWork.getCase();
		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 toWorkUUID = rs.getString("vc_to");
				String toPathUUID = rs.getString("vc_uuid");

				if (rs.next())
				{
					throw new ArbitraryFetchException(sm.getString("work.broadcastDispatched"));
				}

				WorkflowWork toWork =
					caseDatabase.findWork(workflowSession, userTransaction, workflowProcess, workflowCase, toWorkUUID);

				if (toWork.getAcceptTime() != null)
				{
					throw new ArbitraryFetchException(sm.getString("work.alreadyAccepted.to"));
				}

				WorkflowPath path =
					caseDatabase.findPath(workflowSession, userTransaction, workflowProcess, workflowCase, toPathUUID);
				caseDatabase.deletePath(workflowSession, userTransaction, workflowProcess, path);
				caseDatabase.deleteWork(workflowSession, userTransaction, workflowProcess, toWork,"fetch event");

				int o_case_state = workflowCase.getState();
				java.util.Date odispTime = workflowWork.getDispatchTime();
				workflowWork.setDispatchTime(null);

				try
				{
					if (workflowWork.getUUID().equals(workflowCase.getMagicUUID()))
					{
						workflowCase.setState(WorkflowCase.CREATED);
						caseDatabase.updateCase(workflowSession, userTransaction, workflowProcess, workflowCase,"");
					}

					caseDatabase.updateWork(workflowSession, userTransaction, workflowProcess, workflowWork,"");
				}
				catch (WorkflowException ex)
				{
					if (workflowWork.getUUID().equals(workflowCase.getMagicUUID()))
					{
						workflowCase.setState(o_case_state);
					}

					workflowWork.setDispatchTime(odispTime);

					throw ex;
				}
			}
			else
			{
				WorkflowContext context =
					caseDatabase.getWorkflowTemplate().getOwnerWorkflowXPDL().getWorkflowConfig().getWorkflowContext();
				String message = sm.getString("path.notFound", workflowPackage, workflowProcess, workflowCase, workflowWork);
				context.log(message);

				throw new WorkflowException(message);
			}
		}
		catch (SQLException e)
		{
			throw new WorkflowException(e);
		}
		finally
		{
			JDBCUtilities.cleanupNoThrow(pstmt);
		}

		return true;
	}

	public static boolean executeDispatchIfNecessary(
		WorkflowSession workflowSession,
		WorkflowXPDL workflowTemplate,
		WorkflowTransaction userTransaction,
		WorkflowProcess workflowProcess,
		WorkflowWork workflowWork,
		Map userObject)
		throws WorkflowException
	{

		WorkflowActivity workActivity = workflowWork.getActivity();
		Implementation impl = workActivity.getImplementation();
		if ((impl instanceof SubFlowImplementation)
			|| (PerformerType.PERFORMER_AUTOMATION.equals(workActivity.getPerformerType())))
		{
			return accept_core(workflowSession, workflowTemplate, userTransaction, workflowProcess, workflowWork)
				&& dispatch_core(workflowSession, workflowTemplate, userTransaction, workflowProcess, workflowWork, userObject);
		}

		return true;
	}

	private static boolean submit_core(
		WorkflowSession workflowSession,
		WorkflowXPDL workflowTemplate,
		WorkflowTransaction userTransaction,
		WorkflowProcess workflowProcess,
		WorkflowWork workflowWork,
		Map userObject,
		boolean resubmit)
		throws WorkflowException
	{
		if (workflowWork.getDispatchTime() != null)
		{
			throw new WorkflowException(sm.getString("work.alreadyDispatched"));
		}

		if (workflowWork.getAcceptTime() == null)
		{
			throw new WorkflowException(sm.getString("work.alreadyAccepted"));
		}

		WorkflowCaseDatabase caseDatabase = workflowTemplate.getWorkflowTemplate().getCaseDatabase();

		WorkflowActivity workflowActivity = workflowWork.getActivity();

		WorkflowCase workflowCase = workflowWork.getCase();
		WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();
		int ostatus_case = workflowCase.getState();

		workflowWork.setDispatchTime(new java.util.Date());

		try
		{
			if (workflowWork.getUUID().equals(workflowCase.getMagicUUID()))
			{
				workflowCase.setState(WorkflowCase.RUNNING);
				caseDatabase.updateCase(workflowSession, userTransaction, workflowProcess, workflowCase,"");
			}

			caseDatabase.updateWork(workflowSession, userTransaction, workflowProcess, workflowWork,"");

			//------------------------------------------------------------------------------------
			List filterTrans = null;
			if (resubmit)
			{
				WorkflowTransition customizedTransition = null;
				resubmit_prepare(workflowSession, workflowTemplate, userTransaction, workflowProcess, workflowWork);
				if (customizedTransition != null)
				{
					filterTrans = new ArrayList();
					filterTrans.add(customizedTransition);
				}
			}
			else
			{
				filterTrans =
					OutgoingTransitionChooser.chooseOutgingTransitions(workflowSession, caseDatabase, userTransaction, workflowWork);
			}
			//------------------------------------------------------------------------------------

			List paricipant_id_list = Constants.getParticipants(userObject);
			Constants.setParticipants(userObject, new ArrayList());

			int sizeTrans = ((filterTrans == null) ? 0 : filterTrans.size());
			if (sizeTrans > 1 && (paricipant_id_list != null && paricipant_id_list.size() > 1))
			{
				//如果用户显式的提供了本次分发的后继工作项的处理者的话，要求后继分发转移必须唯一
				throw new WorkflowException(sm.getString("transition.canNotHaveMoreThanOne"));
			}

			if (sizeTrans > 0)
			{
				WorkflowTransition transition = null;

				//-------------------------------------------------------------
				//   工作项分发触发的工作项使用相同的分发记号
				//--------------------------------------------------------------
				String pathDispatchBatch =
					workflowTemplate.getWorkflowTemplate().getUUIDGenerator().nextBatchUUID(workflowWork);

				DispatchThread pThreads[] = new DispatchThread[sizeTrans];
				for (int i = 0; i < sizeTrans; ++i)
				{
					transition = (WorkflowTransition) filterTrans.get(i);

					pThreads[i] =
						new DispatchThread(
							workflowSession,
							workflowTemplate,
							userTransaction,
							workflowProcess,
							workflowWork,
							transition,
							pathDispatchBatch,
							userObject);

					pThreads[i].start();
				}

				WorkflowException dispatchException = null;
				for (int i = 0; i < sizeTrans; ++i)
				{
					try
					{
						pThreads[i].join();

						if (pThreads[i].getDispatchException() != null)
						{
							dispatchException = pThreads[i].getDispatchException();
						}
					}
					catch (InterruptedException ex)
					{
						dispatchException = new WorkflowException(sm.getString("dispatchThread.interrupted"));
					}
				}

				if (dispatchException != null)
				{
					throw dispatchException;
				}
			}
			else
			{
				WorkflowContext context =
					caseDatabase.getWorkflowTemplate().getOwnerWorkflowXPDL().getWorkflowConfig().getWorkflowContext();
				String message = sm.getString("transition.notFound", workflowPackage, workflowProcess, workflowActivity);
				context.log(message);

				throw new WorkflowException(message);
			}
		}
		catch (WorkflowException ex)
		{
			workflowWork.setDispatchTime(null);

			if (workflowWork.getUUID().equals(workflowCase.getMagicUUID()))
			{
				workflowCase.setState(ostatus_case);
			}

			throw ex;
		}

		return true;
	}

	private static boolean backtrack_core(
		WorkflowSession workflowSession,
		WorkflowXPDL workflowTemplate,
		WorkflowTransaction userTransaction,
		WorkflowProcess workflowProcess,
		WorkflowWork workflowWork,
		Map userObject)
		throws WorkflowException
	{
		WorkflowCaseDatabase caseDatabase = workflowTemplate.getWorkflowTemplate().getCaseDatabase();

		WorkflowActivity activity = workflowWork.getActivity();

		JoinType joinType = activity.getJoinType();
		if (joinType != null && !JoinType.JOIN_UNKNOWN.equals(joinType) && !JoinType.JOIN_ROUTE.equals(joinType))
		{
			throw new ArbitraryDispatchException(sm.getString("work.arbitraryBacktrack.join"));
		}

		java.util.Date o_work_acceptTime = workflowWork.getAcceptTime();

		try
		{
			String strSQL =
				"SELECT vc_from,vc_batchNo FROM "
					+ userTransaction.getDatabase().getSchemaContext().getTableName(SchemaContext.SCHEMA_PATH)
					+ "  "
					+ "WHERE  vc_packageid=? "
					+ "AND    vc_processid=? "
					+ "AND    vc_caseid=?  "
					+ "AND    vc_to=? ";

			Connection conn = userTransaction.getConnection();
			PreparedStatement pstmt = null;

			WorkflowCase workflowCase = workflowWork.getCase();
			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");

					JDBCUtilities.cleanupNoThrow(pstmt);
					pstmt = null;

					strSQL =
						"SELECT count(*) FROM "
							+ userTransaction.getDatabase().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 ArbitraryDispatchException(sm.getString("work.broadcastDispatched"));
						}
					}
					else
					{
						WorkflowContext context =
							caseDatabase.getWorkflowTemplate().getOwnerWorkflowXPDL().getWorkflowConfig().getWorkflowContext();
						String message =
							sm.getString("path.notFound", workflowPackage, workflowProcess, workflowCase, workflowWork);
						context.log(message);

						throw new WorkflowException(message);
					}
				}
				else
				{
					WorkflowContext context =
						caseDatabase.getWorkflowTemplate().getOwnerWorkflowXPDL().getWorkflowConfig().getWorkflowContext();
					String message = sm.getString("path.notFound", workflowPackage, workflowProcess, workflowCase, workflowWork);
					context.log(message);

					throw new WorkflowException(message);
				}
			}
			catch (SQLException ex)
			{
				throw new WorkflowException(ex);
			}
			finally
			{
				JDBCUtilities.cleanupNoThrow(pstmt);
			}

			java.util.Date now = new java.util.Date();
			if (o_work_acceptTime == null)
			{
				workflowWork.setAcceptTime(now);
			}
			workflowWork.setDispatchTime(now);

			caseDatabase.updateWork(workflowSession, userTransaction, workflowProcess, workflowWork,"");

			WorkflowWork fromWork =
				caseDatabase.findWork(workflowSession, userTransaction, workflowProcess, workflowCase, fromWorkID);

			WorkflowParticipant participant = fromWork.getPerformer();

			String submitWorkId =
				caseDatabase.createWork(
					workflowSession,
					userTransaction,
					workflowProcess,
					workflowCase,
					fromWork.getActivity(),
					null,
					participant,
					workflowWork.getDispatchTime(),
					WorkEvent.BACKTRACKED,
					WorkflowWork.DEFAULT_PRIORITY);

			WorkflowWork submitWork =
				caseDatabase.findWork(workflowSession, userTransaction, workflowProcess, workflowCase, submitWorkId);
			String pathDispatchBatch = workflowTemplate.getWorkflowTemplate().getUUIDGenerator().nextBatchUUID(workflowWork);

			caseDatabase.createPath(
				workflowSession,
				userTransaction,
				workflowProcess,
				workflowCase,
				workflowWork,
				submitWork,
				pathDispatchBatch,
				WorkEvent.BACKTRACKED);
		}
		catch (WorkflowException ex)
		{
			workflowWork.setAcceptTime(o_work_acceptTime);
			workflowWork.setDispatchTime(null);
			throw ex;
		}

		return true;
	}

	private static boolean reverse_core(
		WorkflowSession workflowSession,
		WorkflowXPDL workflowTemplate,
		WorkflowTransaction userTransaction,
		WorkflowProcess workflowProcess,
		WorkflowWork workflowWork,
		Map userObject)
		throws WorkflowException
	{
		WorkflowCaseDatabase caseDatabase = workflowTemplate.getWorkflowTemplate().getCaseDatabase();

		WorkflowActivity activity = workflowWork.getActivity();

		JoinType joinType = activity.getJoinType();
		if (joinType != null && !JoinType.JOIN_UNKNOWN.equals(joinType) && !JoinType.JOIN_ROUTE.equals(joinType))
		{
			throw new ArbitraryDispatchException(sm.getString("joinwork.reverseNotSuppored"));
		}

		java.util.Date o_acceptTime = workflowWork.getAcceptTime();
		try
		{
			String strSQL =
				"SELECT vc_from,vc_batchNo FROM "
					+ userTransaction.getDatabase().getSchemaContext().getTableName(SchemaContext.SCHEMA_PATH)
					+ "  "
					+ "WHERE  vc_packageid=? "
					+ "AND    vc_processid=? "
					+ "AND    vc_caseid=?  "
					+ "AND    vc_to=? ";

			Connection conn = userTransaction.getConnection();
			PreparedStatement pstmt = null;
			ResultSet rs = null;

			WorkflowCase workflowCase = workflowWork.getCase();
			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());

				rs = pstmt.executeQuery();
				if (rs.next())
				{
					fromWorkID = rs.getString("vc_from");
					batchNo = rs.getString("vc_batchNo");

					JDBCUtilities.cleanupNoThrow(pstmt);
					pstmt = null;

					strSQL =
						"SELECT count(*) FROM "
							+ userTransaction.getDatabase().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 ArbitraryDispatchException(sm.getString("work.broadcastDispatched"));
						}
					}
					else
					{
						WorkflowContext context =
							caseDatabase.getWorkflowTemplate().getOwnerWorkflowXPDL().getWorkflowConfig().getWorkflowContext();
						String message =
							sm.getString("path.notFound", workflowPackage, workflowProcess, workflowCase, workflowWork);
						context.log(message);

						throw new WorkflowException(message);
					}
				}
				else
				{
					WorkflowContext context =
						caseDatabase.getWorkflowTemplate().getOwnerWorkflowXPDL().getWorkflowConfig().getWorkflowContext();
					String message = sm.getString("path.notFound", workflowPackage, workflowProcess, workflowCase, workflowWork);
					context.log(message);

					throw new WorkflowException(message);
				}
			}
			catch (SQLException ex)
			{
				throw new WorkflowException(ex);
			}
			finally
			{
				JDBCUtilities.cleanupNoThrow(pstmt);
			}

			//----------------------------------------------------------------------------------
			List workList =
				reverse_prepare(workflowSession, workflowTemplate, userTransaction, workflowProcess, workflowWork);
			//----------------------------------------------------------------------------------

			if (workList == null || workList.size() == 0)
			{
				throw new WorkflowException();
			}

			java.util.Date now = new java.util.Date();
			workflowWork.setDispatchTime(now);
			if (workflowWork.getAcceptTime() == null)
			{
				workflowWork.setAcceptTime(now);
			}

			caseDatabase.updateWork(workflowSession, userTransaction, workflowProcess, workflowWork,"");

			int workSize = workList.size();

			for (int i = 0; i < workSize; ++i)
			{
				WorkflowWork fromWork = (WorkflowWork) workList.get(i);

				WorkflowParticipant participant = fromWork.getPerformer();

				String submitWorkId =
					caseDatabase.createWork(
						workflowSession,
						userTransaction,
						workflowProcess,
						workflowCase,
						fromWork.getActivity(),
						null,
						participant,
						workflowWork.getDispatchTime(),
						WorkEvent.REVERSED,
						WorkflowWork.DEFAULT_PRIORITY);

				WorkflowWork submitWork =
					caseDatabase.findWork(workflowSession, userTransaction, workflowProcess, workflowCase, submitWorkId);
				String pathDispatchBatch =
					workflowTemplate.getWorkflowTemplate().getUUIDGenerator().nextBatchUUID(workflowWork);

				caseDatabase.createPath(
					workflowSession,
					userTransaction,
					workflowProcess,
					workflowCase,
					workflowWork,
					submitWork,
					pathDispatchBatch,
					WorkEvent.REVERSED);
			}

		}
		catch (WorkflowException ex)
		{
			workflowWork.setAcceptTime(o_acceptTime);
			workflowWork.setDispatchTime(null);
			throw ex;
		}

		return true;
	}

	private static WorkflowTransition resubmit_prepare(
		WorkflowSession workflowSession,
		WorkflowXPDL workflowTemplate,
		WorkflowTransaction userTransaction,
		WorkflowProcess workflowProcess,
		WorkflowWork workflowWork)
		throws WorkflowException
	{
		WorkflowCaseDatabase caseDatabase = workflowTemplate.getWorkflowTemplate().getCaseDatabase();

		WorkflowActivity workflowActivity = workflowWork.getActivity();
		WorkflowCase workflowCase = workflowWork.getCase();
		WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();

		WorkflowTransition resbumit_transition = null;

		String strSQL =
			"SELECT    vc_from,vc_owner_processid,vc_transitionid FROM "
				+ userTransaction.getDatabase().getSchemaContext().getTableName(SchemaContext.SCHEMA_PATH)
				+ "  "
				+ "WHERE  vc_packageid=? "
				+ " "
				+ "AND    vc_processid=? "
				+ " "
				+ "AND    vc_caseid=?  "
				+ " "
				+ "AND    vc_to=? "
				+ " "
				+ "AND    ( int_eventType=? "
				+ "OR  int_eventType=? )";

		Connection conn = userTransaction.getConnection();
		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, WorkEvent.BACKTRACKED); //<-----very important
			pstmt.setInt(6, WorkEvent.REVERSED); //<-----very important

			ResultSet rs = pstmt.executeQuery();
			if (rs.next())
			{
				String vc_owner_processid = rs.getString("vc_owner_processid");
				String vc_transitionid = rs.getString("vc_transitionid");

				WorkflowWork fromWork =
					caseDatabase.findWork(
						workflowSession,
						userTransaction,
						workflowProcess,
						workflowCase,
						rs.getString("vc_from"));

				if (vc_owner_processid != null && vc_owner_processid.length() > 0)
				{
					WorkflowProcess owner_processid = workflowPackage.findWorkflowProcess(vc_owner_processid);
					resbumit_transition = owner_processid.findTransition(vc_transitionid);
				}
				else
				{
					resbumit_transition = workflowProcess.findTransition(workflowWork.getActivity(), fromWork.getActivity());
				}

				List paricipant_id_list = new ArrayList();
				paricipant_id_list.add(fromWork.getPerformer().getUUID());
				workflowCase.getCaseContext().setPerformersByTransition(resbumit_transition, paricipant_id_list);
			}
		}
		catch (SQLException e)
		{
			WorkflowException ex = new WorkflowException("can not revert workflow workflowWork");
			ex.setRootCause(e);
			throw ex;
		}
		finally
		{
			JDBCUtilities.cleanupNoThrow(pstmt);
		}

		return resbumit_transition;
	}

	private static List reverse_prepare(
		WorkflowSession workflowSession,
		WorkflowXPDL workflowTemplate,
		WorkflowTransaction userTransaction,
		WorkflowProcess workflowProcess,
		WorkflowWork workflowWork)
		throws WorkflowException
	{
		WorkflowActivity workflowActivity = workflowWork.getActivity();
		WorkflowCase workflowCase = workflowWork.getCase();

		WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();
		WorkflowProcess mainWorkflowProcess = workflowProcess;
		WorkflowProcess onwerWorkflowProcess = workflowActivity.getWorkflowProcess();

		List incomingTransitions = workflowActivity.getIncomingTransitions();

		String strSQL =
			"SELECT vc_uuid FROM "
				+ userTransaction.getDatabase().getSchemaContext().getTableName(SchemaContext.SCHEMA_WORK)
				+ "  "
				+ "WHERE  vc_packageid=? "
				+ " "
				+ "AND    vc_processid=? "
				+ " "
				+ "AND    vc_activityid=?  "
				+ " "
				+ "AND    vc_caseid=?  "
				+ " "
				+ "AND   ( dat_dispatchTime IS NOT NULL ) "
				+ " "
				+ "ORDER BY dat_arriveTime DESC";

		Connection conn = userTransaction.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		WorkflowCaseDatabase caseDatabase = workflowTemplate.getWorkflowTemplate().getCaseDatabase();

		List workList = new ArrayList();

		try
		{
			pstmt = conn.prepareStatement(strSQL);
			pstmt.setString(1, workflowPackage.getUUID());
			pstmt.setString(2, workflowProcess.getUUID());
			pstmt.setString(3, workflowActivity.getUUID());
			pstmt.setString(4, workflowCase.getUUID());

			rs = pstmt.executeQuery();
			rs.next(); //don't check 			
			String workid = rs.getString("vc_uuid");

			log.debug("workid ==>" + workid);

			JDBCUtilities.cleanupNoThrow(pstmt, rs);

			strSQL =
				"SELECT    vc_from FROM "
					+ userTransaction.getDatabase().getSchemaContext().getTableName(SchemaContext.SCHEMA_PATH)
					+ "  "
					+ "WHERE  vc_packageid=? "
					+ " "
					+ "AND    vc_processid=? "
					+ " "
					+ "AND    vc_caseid=?  "
					+ " "
					+ "AND    vc_to=? ";

			pstmt = conn.prepareStatement(strSQL);
			pstmt.setString(1, workflowPackage.getUUID());
			pstmt.setString(2, workflowProcess.getUUID());
			pstmt.setString(3, workflowCase.getUUID());
			pstmt.setString(4, workid);

			rs = pstmt.executeQuery();

			while (rs.next())
			{
				log.debug("workid(vc_from) ==>" + rs.getString("vc_from"));

				WorkflowWork fromwork =
					caseDatabase.findWork(
						workflowSession,
						userTransaction,
						workflowProcess,
						workflowCase,
						rs.getString("vc_from"));

				if (fromwork != null)
				{
					workList.add(fromwork);
				}
			}
		}
		catch (SQLException ex)
		{
			throw new WorkflowException(ex);
		}
		finally
		{
			JDBCUtilities.cleanupNoThrow(pstmt, rs);
		}

		log.debug("==>" + workList);

		return workList;
	}

	//---------------------------------------------------------------------------
	//parallel execution thread class
	//---------------------------------------------------------------------------
	static class DispatchThread extends Thread
	{
		//==========================================================
		private WorkflowSession workflowSession;
		private WorkflowXPDL workflowTemplate;
		private WorkflowTransaction userTransaction;
		private WorkflowProcess workflowProcess;
		private WorkflowWork workflowWork;
		private WorkflowTransition workflowTransition;
		private String pathDispatchBatch;

		private Map userObject;

		//==========================================================
		private WorkflowException dispatchException;
		//==========================================================  

		public DispatchThread(
			WorkflowSession workflowSession,
			WorkflowXPDL workflowTemplate,
			WorkflowTransaction userTransaction,
			WorkflowProcess workflowProcess,
			WorkflowWork workflowWork,
			WorkflowTransition workflowTransition,
			String pathDispatchBatch,
			Map userObject)
		{
			this.workflowSession = workflowSession;
			this.workflowTemplate = workflowTemplate;
			this.userTransaction = userTransaction;
			this.workflowProcess = workflowProcess;

			this.workflowWork = workflowWork;
			this.workflowTransition = workflowTransition;
			this.pathDispatchBatch = pathDispatchBatch;
			this.userObject = userObject;
		}

		public void run()
		{
			try
			{
				dispatchRun();
			}
			catch (WorkflowException workflowException)
			{
				WorkflowContext context = workflowTemplate.getWorkflowConfig().getWorkflowContext();
				context.log("ParallelThread invokes dispatchRun ", workflowException);
				dispatchException = workflowException;
			}
			catch (InterruptedException interruptedException)
			{
				WorkflowContext context = workflowTemplate.getWorkflowConfig().getWorkflowContext();
				context.log("ParallelThread invokes dispatchRun ", interruptedException);
				dispatchException = new WorkflowException(interruptedException);
			}
		}

		public WorkflowException getDispatchException()
		{
			return dispatchException;
		}

		private boolean dispatchRun() throws WorkflowException, InterruptedException
		{
			//log.debug("[submit_control] workitem: " + workflowWork.getInfo() + " | transition: " + workflowTransition.getInfo());
			WorkflowCaseDatabase caseDatabase = workflowTemplate.getWorkflowTemplate().getCaseDatabase();

			WorkflowActivity fromActivity = workflowWork.getActivity();
			WorkflowActivity toActivity = workflowTransition.getToActivity();

			Implementation impl = toActivity.getImplementation();
			if (impl instanceof SubFlowImplementation)
			{
				//-----------------------------------------------------------
				// 进入子流程处理逻辑
				//-----------------------------------------------------------
				return CommonDispatcher.submit_subflow(
					workflowSession,
					workflowTemplate,
					userTransaction,
					workflowProcess,
					workflowWork,
					workflowTransition,
					pathDispatchBatch,
					userObject);
			}

			WorkflowActivity ownerActivity = workflowWork.getActivity();
			WorkflowProcess ownerProcess = ownerActivity.getWorkflowProcess();
			WorkflowActivity endActivity = WorkflowUtilities.getEndActivity(ownerProcess);

			if (endActivity.getUUID().equals(toActivity.getUUID()))
			{
				if (workflowWork.getParentWork() != null)
				{
					return CommonDispatcher.submit_r2p(
						workflowSession,
						workflowTemplate,
						userTransaction,
						workflowProcess,
						workflowWork,
						workflowTransition,
						pathDispatchBatch,
						userObject);
				}
				else
				{
					return CommonDispatcher.submit_endflow(
						workflowSession,
						workflowTemplate,
						userTransaction,
						workflowProcess,
						workflowWork,
						workflowTransition,
						pathDispatchBatch,
						userObject);
				}
			}
			else
			{
				JoinType joinType = toActivity.getJoinType();
				if (joinType != null && !JoinType.JOIN_UNKNOWN.equals(joinType))
				{
					return JoinActivityDispatcher.submit_join(
						workflowSession,
						workflowTemplate,
						userTransaction,
						workflowProcess,
						workflowWork,
						workflowTransition,
						pathDispatchBatch,
						userObject);
				}
				else
				{
					return CommonDispatcher.submit_general(
						workflowSession,
						workflowTemplate,
						userTransaction,
						workflowProcess,
						workflowWork,
						workflowTransition,
						pathDispatchBatch,
						userObject);
				}
			}
		}
	}
}
