/* $Id: ProcessInternal.java,v 1.3 2007/12/05 05:56:17 nito Exp $
 *
 * Copyright (c)ARGO 21, Corporation. 2005, 2006.  All rights reserved.
 * 
 * This file is part of Nautica Workflow Core.
 * 
 *  Nautica Workflow Core is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2.1 of the License, or
 *  (at your option) any later version.
 * 
 *  Nautica Workflow Core is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Nautica Workflow Core; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 */
package jp.co.argo21.nautica.workflow.engine;

import java.sql.Timestamp;
import java.util.List;

import org.apache.log4j.Logger;

import jp.co.argo21.nautica.workflow.dataaccess.ActivityBean;
import jp.co.argo21.nautica.workflow.dataaccess.ActivityDAO;
import jp.co.argo21.nautica.workflow.dataaccess.ProcessBean;
import jp.co.argo21.nautica.workflow.dataaccess.ProcessDAO;
import jp.co.argo21.nautica.workflow.dataaccess.ProcessWaitActivityBean;
import jp.co.argo21.nautica.workflow.dataaccess.ProcessWaitActivityDAO;
import jp.co.argo21.nautica.workflow.dataaccess.VariableBean;
import jp.co.argo21.nautica.workflow.dataaccess.VariableDAO;
import jp.co.argo21.nautica.workflow.dataaccess.WorkflowDAOFactory;
import jp.co.argo21.nautica.workflow.definition.ActivityDefinition;
import jp.co.argo21.nautica.workflow.definition.ProcessDefinition;
import jp.co.argo21.nautica.workflow.jms.WorkflowMessage;
import jp.co.argo21.nautica.workflow.omg.AlreadyRunningException;
import jp.co.argo21.nautica.workflow.omg.AlreadySuspendedException;
import jp.co.argo21.nautica.workflow.omg.CannotChangeRequesterException;
import jp.co.argo21.nautica.workflow.omg.CannotResumeException;
import jp.co.argo21.nautica.workflow.omg.CannotStartException;
import jp.co.argo21.nautica.workflow.omg.CannotStopException;
import jp.co.argo21.nautica.workflow.omg.CannotSuspendException;
import jp.co.argo21.nautica.workflow.omg.HistoryNotAvailableException;
import jp.co.argo21.nautica.workflow.omg.InvalidDataException;
import jp.co.argo21.nautica.workflow.omg.InvalidStateException;
import jp.co.argo21.nautica.workflow.omg.NotRunningException;
import jp.co.argo21.nautica.workflow.omg.NotSuspendedException;
import jp.co.argo21.nautica.workflow.omg.ResultNotAvailableException;
import jp.co.argo21.nautica.workflow.omg.TransitionNotAllowedException;
import jp.co.argo21.nautica.workflow.omg.UpdateNotAllowedException;
import jp.co.argo21.nautica.workflow.omg.WfActivity;
import jp.co.argo21.nautica.workflow.omg.WfEvent;
import jp.co.argo21.nautica.workflow.omg.WfNameValue;
import jp.co.argo21.nautica.workflow.omg.WfProcess;
import jp.co.argo21.nautica.workflow.omg.WfProcessManager;
import jp.co.argo21.nautica.workflow.omg.WfRequester;
import jp.co.argo21.nautica.workflow.omg.WfState;
import jp.co.argo21.nautica.workflow.omg.WorkflowException;
import jp.co.argo21.nautica.workflow.util.DurationHelper;
import jp.co.argo21.nautica.workflow.util.StringManager;
import jp.co.argo21.nautica.workflow.util.UniqueKeyGenerator;
import jp.co.argo21.nautica.workflow.wfmc.Activity;
import jp.co.argo21.nautica.workflow.wfmc.ActivityState;
import jp.co.argo21.nautica.workflow.wfmc.Attribute;
import jp.co.argo21.nautica.workflow.wfmc.Filter;
import jp.co.argo21.nautica.workflow.wfmc.FormalParameter;
import jp.co.argo21.nautica.workflow.wfmc.InvalidFilterException;
import jp.co.argo21.nautica.workflow.wfmc.Process;
import jp.co.argo21.nautica.workflow.wfmc.ProcessState;

/**
 * エンジン内部で使用されるプロセスインスタンス情報。
 *
 * @author  nito(Argo 21, Corp.)
 * @version $Revision: 1.3 $
 * @since   Nautica Workflow 1.0
 */
class ProcessInternal implements WfProcess
{
	/** シリアルバージョンUID */
	private static final long serialVersionUID = 20050000000000001L;

	/** エンジンログ */
	private static Logger eLog = LogManager.getEngineLogger();
	
	/** プロセス */
	private Process process;
	
	/** パラメータ情報 */
	@SuppressWarnings("unused")
	private FormalParameter[] params;
	
	/** リクエスタ */
	private WfRequester requester;

	/**
	 * 内部用プロセス情報を生成する。
	 *
	 * @param proc プロセス
	 * @param params パラメータ
	 */
	public ProcessInternal(Process proc, FormalParameter[] params)
	{
		this.process = proc;
		this.params = params;
	}

	/**
	 * プロセス要求者を返す。
	 *
	 * @return プロセス要求者
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#getRequester()
	 */
	public WfRequester getRequester() throws WorkflowException
	{
		return requester;
	}

	/**
	 * プロセスは、WfProcessMgrに関連付けられる。
	 * WfProcessが生成される時点で関連付けが行われ、変更はできない。
	 *
	 * @return プロセスマネージャ
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#getManager()
	 */
	public WfProcessManager getManager() throws WorkflowException
	{
		ProcessManagerFactory pmf = ProcessManagerFactory.getInstance();
		WfProcessManager pm = pmf.getOwnerProcessManager(process.getID());
		return pm;
	}

	/**
	 * IDを返す。
	 *
	 * @return ID
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getID()
	 */
	public String getID() throws WorkflowException
	{
		return process.getID();
	}

	/**
	 * 名称を返す。
	 *
	 * @return 名称
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getName()
	 */
	public String getName() throws WorkflowException
	{
		return process.getName();
	}

	/**
	 * 詳細情報を返す。
	 *
	 * @return 詳細情報
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getDescription()
	 */
	public String getDescription() throws WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * 現在の状態を返す。
	 *
	 * @return 状態
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getState()
	 */
	public WfState getState() throws WorkflowException
	{
		return ProcessState.getState(process.getProcessState());
	}

	/**
	 * 現在の状態から遷移可能な状態の一覧を返す。
	 *
	 * @return 遷移可能な状態の一覧
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getTransitableStates()
	 */
	public WfState[] getTransitableStates() throws WorkflowException
	{
		return ProcessState.getTransitableStates(process.getProcessState());
	}

	/**
	 * 最後に状態が遷移した時間を返す。
	 *
	 * @return 最終状態変更時間
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getLastStateTime()
	 */
	public String getLastStateTime() throws WorkflowException
	{
		return process.getUpdateDate().toString();
	}

	/**
	 * 実行オブジェクトにかかわる属性一覧を返す。
	 *
	 * @return 属性一覧
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getContext()
	 */
	public WfNameValue[] getContext() throws WorkflowException
	{
		try {
			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			VariableDAO variableDAO = daoFactory.getVariableDAO();
			
			VariableBean[] beans = variableDAO.findByProcessID(process.getID());
			
			return beans;
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセス変数の取得に失敗しました。
			String E0032 = StringManager.get("E0032");
			throw new WorkflowException(E0032, ex);
		}
	}

	/**
	 * 実行オブジェクトにかかわる属性を返す。
	 *
	 * @param name 属性名
	 * @return 属性
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getContext()
	 */
	public WfNameValue getContext(String name) throws WorkflowException
	{
		try {
			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			VariableDAO variableDAO = daoFactory.getVariableDAO();
			
			VariableBean beans = variableDAO.findByName(process.getID(), name);
			
			return beans;
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセス変数の取得に失敗しました。
			String E0032 = StringManager.get("E0032");
			throw new WorkflowException(E0032, ex);
		}
	}

	/**
	 * WfProcessによって生成される結果を返す。
	 * プロセスが完了するまでは結果は定義されない。
	 * 結果の取得がまだ取得できない場合は、例外が発生する。
	 *
	 * @return 結果
	 * @throws ResultNotAvailableException 結果を取得できない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#getResult()
	 */
	public WfNameValue[] getResult() throws ResultNotAvailableException,
			WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * 変更履歴の数を返す。
	 *
	 * @return 変更履歴の数
	 * @throws HistoryNotAvailableException 変更履歴が取得可能でない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getHistoryCount()
	 */
	public int getHistoryCount() throws HistoryNotAvailableException,
			WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * インデクスを指定して、履歴を取得する。
	 *
	 * @param index インデクス
	 * @return 変更履歴
	 * @throws HistoryNotAvailableException 変更履歴が取得可能でない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getHistory(int)
	 */
	public WfEvent getHistory(int index) throws HistoryNotAvailableException,
			WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * この実行オブジェクトに関わる変更履歴を取得する。
	 *
	 * @return 変更履歴の数
	 * @throws HistoryNotAvailableException 変更履歴が取得可能でない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#getAllHistories()
	 */
	public WfEvent[] getAllHistories() throws HistoryNotAvailableException,
			WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * 引数で指定された状態を持つアクティビティを返す。
	 *
	 * @param state 状態
	 * @return アクティビティ
	 * @throws InvalidStateException 不正な状態
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#getActivitiesInState(java.lang.String)
	 */
	public WfActivity[] getActivitiesInState(String state)
			throws InvalidStateException, WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * 現時点の全ステップを返す。
	 *
	 * @return 全ステップ
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#getAllSteps()
	 */
	public WfActivity[] getAllSteps() throws WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * ステップからIDで指定されたアクティビティを返す。
	 *
	 * @param aid アクティビティID
	 * @return アクティビティ
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @throws WorkflowException
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#getStep(java.lang.String)
	 */
	public WfActivity getStep(String aid) throws WorkflowException
	{
		try {
			WorkflowDAOFactory daoFactory = DataAccessManager.getDAOFactory();
			ActivityDAO actDAO = daoFactory.getActivityDAO();
			
			ActivityBean bean = actDAO.findByPrimaryKey(process.getID(), aid);
			ActivityInternal act = null;
			if (bean != null) {
				act = new ActivityInternal(bean);
			}
			return act;
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// アクティビティの取得に失敗しました。
			String E0124 = StringManager.get("E0124") + "(" + aid + ")";
			throw new WorkflowException(E0124, ex);
		}
	}

	/**
	 * 0以上のWfActiviyがプロセスに関連付けられる。
	 * ステップは、現時点でプロセス内でopenな状態であるアクティビティを表すものとする。
	 * ここでは、ステップの個数を返す。
	 *
	 * @return ステップの個数
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#getStepCount()
	 */
	public int getStepCount() throws WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * 引数で指定されたアクティビティが、そのプロセスのステップに所属しているかどうかを返す。
	 *
	 * @param act アクティビティ
	 * @return 所属している場合はtrue
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#isMemberOfStep(jp.co.argo21.nautica.workflow.omg.WfActivity)
	 */
	public boolean isMemberOfStep(WfActivity act) throws WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * 名称を設定する。
	 *
	 * @param name 名称
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#setName(java.lang.String)
	 */
	public void setName(String name) throws WorkflowException
	{
		try {
			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			ProcessDAO processDAO = daoFactory.getProcessDAO();
			
			processDAO.updateName((ProcessBean)process, name);
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセス名の設定に失敗しました。
			String E0125 = StringManager.get("E0125");
			throw new WorkflowException(E0125, ex);
		}
	}

	/**
	 * プロセス要求者をプロセスに関連付ける。
	 * プロセスが生成された時点で、この関連付けが行われる。
	 * プロセスは、WfRequesterのインタフェースを通じて、状態の変更を通知する。
	 *
	 * @param requester プロセス要求者
	 * @throws CannotChangeRequesterException プロセス要求者を更新できない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#setRequester(jp.co.argo21.nautica.workflow.omg.WfRequester)
	 */
	public void setRequester(WfRequester requester)
			throws CannotChangeRequesterException, WorkflowException
	{
		this.requester = requester;
	}

	/**
	 * 詳細情報を設定する。
	 *
	 * @param desc 詳細情報
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#setDescription(java.lang.String)
	 */
	public void setDescription(String desc) throws WorkflowException
	{
		String E0900 = StringManager.get("E0900");
		throw new UnsupportedOperationException(E0900);
	}

	/**
	 * 実行オブジェクトにかかわる属性一覧を設定する。
	 *
	 * @param context 属性一覧
	 * @throws InvalidDataException 無効な属性の場合 
	 * @throws UpdateNotAllowedException 更新ができない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#setContext(jp.co.argo21.nautica.workflow.omg.WfNameValue[])
	 */
	public void setContext(WfNameValue[] context)
	throws InvalidDataException, UpdateNotAllowedException, WorkflowException
	{
		try {
			Timestamp ts = DataAccessManager.getAccessTime();
			
			setContext(context, ts);

		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセス変数の設定に失敗しました。
			String E0080 = StringManager.get("E0080");
			throw new WorkflowException(E0080, ex);
		}
	}

	/**
	 * 実行オブジェクトにかかわる属性を設定する。
	 *
	 * @param context 属性
	 * @throws InvalidDataException 無効な属性の場合 
	 * @throws UpdateNotAllowedException 更新ができない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#setContext(jp.co.argo21.nautica.workflow.omg.WfNameValue[])
	 */
	public void setContext(WfNameValue context)
	throws InvalidDataException, UpdateNotAllowedException, WorkflowException
	{
		try {
			Timestamp ts = DataAccessManager.getAccessTime();
			
			setContext(context, ts);
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセス変数の設定に失敗しました。
			String E0080 = StringManager.get("E0080");
			throw new WorkflowException(E0080, ex);
		}
	}

	/**
	 * 指定された状態に遷移を試みる。
	 *
	 * @param state 状態
	 * @throws InvalidStateException 無効な状態の場合
	 * @throws TransitionNotAllowedException 指定された状態に遷移できない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#changeState(jp.co.argo21.nautica.workflow.omg.WfState)
	 */
	public void changeState(WfState state)
	throws InvalidStateException, TransitionNotAllowedException, WorkflowException
	{
		if (state instanceof ProcessState == false) {
			// プロセス状態ではありません。
			String E0126 = StringManager.get("E0126");
			throw new InvalidStateException(E0126);
		}		

		if (ProcessState.OPEN_NOT_RUNNING_NOT_STARTED.equals(state)) {
			// 無効なプロセス状態が指定されました。
			String E0127 = StringManager.get("E0127");
			throw new TransitionNotAllowedException(E0127);
		} else if (ProcessState.OPEN_NOT_RUNNING_SUSPENDED.equals(state)) {
			suspend();
		} else if (ProcessState.OPEN_RUNNING.equals(state)) {
			if (ProcessState.OPEN_NOT_RUNNING_NOT_STARTED.equals(getState())) {
				start();
			} else {
				resume();
			}
		} else if (ProcessState.CLOSED_COMPLETED.equals(state)) {
			complete();
		} else if (ProcessState.CLOSED_TERMINATED.equals(state)) {
			terminate();
		} else if (ProcessState.CLOSED_ABORTED.equals(state)) {
			abort();
		}

		//エンジン間接続を利用している場合
		String pid = process.getID();
		String useInterWorkflow = SystemChecker.getProperty(WorkflowEngineConstants.NAUTICA_INTER_WORKFLOW);
		if (useInterWorkflow != null && useInterWorkflow.equals("enable")) {
			InterWorkflowConnectorImpl iwc =
				(InterWorkflowConnectorImpl)InterWorkflowConnectorImpl.getInstance();
			//利用している場合は必要に応じてリモートのエンジンに通知する
			iwc.processStateChangedLocal(pid, (ProcessState)state);
		}
	}

	/**
	 * WfProcessの実行を開始するために使用される。
	 * プロセスの状態は、open.not_runningからopen.runningに変化する。
	 *
	 * @throws CannotStartException 開始できない場合
	 * @throws AlreadyRunningException すでに開始している場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfProcess#start()
	 */
	public void start()
	throws CannotStartException, AlreadyRunningException, WorkflowException
	{
		try {
			ProcessState state = (ProcessState)getState();
			
			if (ProcessState.CLOSED_COMPLETED.equals(state)
					|| ProcessState.CLOSED_TERMINATED.equals(state)
					|| ProcessState.CLOSED_ABORTED.equals(state)) {
				// プロセスはすでに終了しています。
				String E0128 = StringManager.get("E0128");
				throw new NotRunningException(E0128);
			} else if (ProcessState.OPEN_NOT_RUNNING_NOT_STARTED.equals(state) == false) {
				// プロセスはすでに開始しています。
				String E0129 = StringManager.get("E0129");
				throw new AlreadyRunningException(E0129);
			}

			ProcessBean pbean = getBean();
			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			ProcessDAO procDAO = daoFactory.getProcessDAO();
			Timestamp ts = DataAccessManager.getAccessTime();
			procDAO.updateState(pbean, ProcessState.OPEN_RUNNING, ts);

			//対応するプロセス定義の取得
			String pdid = process.getProcDefinitionID();
			WorkflowEngine engine = WorkflowEngine.getInstance();
			DefinitionRepositoryImpl rep = engine.getDefinitionRepository();
			ProcessDefinition pd = rep.getProcessDefinition(pdid);
			
			//開始アクティビティ定義の探索
			ActivityDefinition ad = findStartActivity(pd);

			//開始アクティビティの登録
			ActivityBean abean = createActivity(pd, ad, pbean);
			
			//メッセージキューに登録
			String pid = abean.getProcessID();
			String aid = abean.getActivityID();
			WorkflowMessage sarmsg = new StartActivityRequestMessage(pid, aid);
			SystemMessageTransporter mt = new SystemMessageTransporter(StartActivityRequestWatcher.NAME);
			mt.sendMessage(sarmsg);

		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセスの開始に失敗しました。
		    String E0132 = StringManager.get("E0132");
			throw new WorkflowException(E0132, ex);
		}
	}

	/**
	 * 保留中の実行オブジェクトの再開を要求する。
	 * 要求が受け入れられた場合、状態は、open.not_running.suspendedから、
	 * open.runningに設定される。
	 *
	 * @throws CannotResumeException 再開できない場合 
	 * @throws NotRunningException 走行中でない場合
	 * @throws NotSuspendedException 保留されていない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#resume()
	 */
	public void resume()
	throws CannotResumeException, NotRunningException,
		NotSuspendedException, WorkflowException
	{
		try {
			ProcessState state = (ProcessState)getState();
			
			if (ProcessState.CLOSED_COMPLETED.equals(state)
					|| ProcessState.CLOSED_TERMINATED.equals(state)
					|| ProcessState.CLOSED_ABORTED.equals(state)) {
				// プロセスはすでに終了しています。
				String E0128 = StringManager.get("E0128");
				throw new NotRunningException(E0128);
			} else if (ProcessState.OPEN_NOT_RUNNING_SUSPENDED.equals(state) == false) {
				// プロセスは保留されていません。
				String E0138 = StringManager.get("E0138");
				throw new NotSuspendedException(E0138);
			}

			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			ProcessDAO procDAO = daoFactory.getProcessDAO();
			Timestamp ts = DataAccessManager.getAccessTime();

			procDAO.updateState(getBean(), ProcessState.OPEN_RUNNING, ts);
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセスの再開に失敗しました。
			String E0133 = StringManager.get("E0133");
			throw new WorkflowException(E0133, ex);
		}
	}

	/**
	 * 実行オブジェクトの保留を要求する。
	 * 要求が受け入れられた場合、状態は、open.not_running.suspendedに設定される。
	 *
	 * @throws CannotSuspendException 保留できない場合
	 * @throws NotRunningException 走行中でない場合
	 * @throws AlreadySuspendedException すでに保留されている場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#suspend()
	 */
	public void suspend()
	throws CannotSuspendException, NotRunningException,
		AlreadySuspendedException, WorkflowException
	{
		try {
			ProcessState state = (ProcessState)getState();
			
			if (ProcessState.CLOSED_COMPLETED.equals(state)
					|| ProcessState.CLOSED_TERMINATED.equals(state)
					|| ProcessState.CLOSED_ABORTED.equals(state)) {
				// プロセスはすでに終了しています。
				String E0128 = StringManager.get("E0128");
				throw new NotRunningException(E0128);
			} else if (ProcessState.OPEN_NOT_RUNNING_SUSPENDED.equals(state)) {
				// プロセスはすでに保留されています。
				String E0130 = StringManager.get("E0130");
				throw new AlreadySuspendedException(E0130);
			}

			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			ProcessDAO procDAO = daoFactory.getProcessDAO();
			Timestamp ts = DataAccessManager.getAccessTime();

			procDAO.updateState(getBean(), ProcessState.OPEN_NOT_RUNNING_SUSPENDED, ts);
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセスの保留に失敗しました。
			String E0134 = StringManager.get("E0134");
			throw new WorkflowException(E0134, ex);
		}
	}

	/**
	 * プロセスの完了を要求するために、アプリケーションによって使用される。
	 *
	 * @throws CannotStopException 完了できない場合
	 * @throws NotRunningException プロセスが走行中でない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 */
	public void complete()
	throws CannotStopException, NotRunningException, WorkflowException
	{
		try {
			boolean closedAlready = false;
			ProcessState state = (ProcessState)getState();
			
			if (ProcessState.CLOSED_COMPLETED.equals(state)
					|| ProcessState.CLOSED_TERMINATED.equals(state)
					|| ProcessState.CLOSED_ABORTED.equals(state)) {
				closedAlready = true;
			}

			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			ProcessDAO procDAO = daoFactory.getProcessDAO();
			Timestamp ts = DataAccessManager.getAccessTime();
			if (closedAlready) {
				//すでにほかのアクティビティにより完了しているため、
				//完了状態は変更せずに日付のみを更新する。
				procDAO.updateDate(getBean(), ts);
			} else {
				procDAO.updateState(getBean(), ProcessState.CLOSED_COMPLETED, ts);
			}

			String pid = process.getID();
			ProcessWaitActivityDAO pwDAO = daoFactory.getProcessWaitActivityDAO();
			ProcessWaitActivityBean pwbean = pwDAO.findBySubprocessID(pid);
			
			//待ち合わせている親アクティビティがある場合、完了要求を出す。
			if (pwbean != null && closedAlready == false) {
				String rpid = pwbean.getProcID();
				String raid = pwbean.getActID();
				//メッセージキューに完了要求登録
				WorkflowMessage earmsg = new EndActivityRequestMessage(rpid, raid);
				SystemMessageTransporter mt = new SystemMessageTransporter(EndActivityRequestWatcher.NAME);
				mt.sendMessage(earmsg);
			} else {
				//プロセス完了時、起動者に通知必要だが、ログ出力に留める。
				// プロセスが完了しました。
				String I0047 = StringManager.get("I0047") + "(" + pid + ")";
				eLog.info(I0047);
			}
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセスの完了に失敗しました。
			String E0135 = StringManager.get("E0135");
			throw new WorkflowException(E0135, ex);
		}
	}

	/**
	 * 実行中の実行オブジェクトの停止を要求する。
	 * 要求が受け入れられた場合、状態はclosed.terminatedに設定される。
	 *
	 * @throws CannotStopException 停止できない場合
	 * @throws NotRunningException 走行中でない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#terminate()
	 */
	public void terminate()
	throws CannotStopException, NotRunningException, WorkflowException
	{
		terminate(true);
	}

	/**
	 * 保留された実行オブジェクトの中断を要求する。
	 * 要求が受け入れられた場合、状態はclosed.abortedに設定される。
	 *
	 * @throws CannotStopException 停止できない場合
	 * @throws NotRunningException 走行中でない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 * @see jp.co.argo21.nautica.workflow.omg.WfExecutionObject#abort()
	 */
	public void abort()
	throws CannotStopException, NotRunningException, WorkflowException
	{
		abort(true);
	}

	/**
	 * フィルターで特定されたプロセス変数を返す。
	 *
	 * @param filter フィルター
	 * @return プロセス変数
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 */
	WfNameValue[] getContext(Filter filter) throws WorkflowException
	{
		try {
			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			VariableDAO variableDAO = daoFactory.getVariableDAO();
			
			VariableBean[] beans = variableDAO.findByFilter(filter, process.getID());
			
			return beans;
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセス変数の取得に失敗しました。
			String E0032 = StringManager.get("E0032");
			throw new WorkflowException(E0032, ex);
		}
	}

	/**
	 * 実行オブジェクトにかかわる属性一覧を設定する。
	 *
	 * @param context 属性一覧
	 * @param ts タイムスタンプ
	 * @throws InvalidDataException 無効な属性の場合 
	 * @throws UpdateNotAllowedException 更新ができない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 */
	void setContext(WfNameValue[] context, Timestamp ts)
	throws InvalidDataException, UpdateNotAllowedException, WorkflowException
	{
		if (context == null) {
			// プロセス変数が未設定です。
			String E0131 = StringManager.get("E0131");
			throw new InvalidDataException(E0131);
		}

		try {
			String pid = process.getID();
			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			VariableDAO variableDAO = daoFactory.getVariableDAO();

			for (int i = 0; i < context.length; i++) {
				if (context[i] instanceof Attribute == false) {
					// プロセス変数として認識できない値が格納されています。
					String E0139 = StringManager.get("E0139");
					throw new InvalidDataException(E0139);
				}
				Attribute attr = (Attribute)context[i];
				VariableBean bean = new VariableBean();
				bean.setName(attr.getName());
				bean.setType(attr.getType());
				bean.setValue(attr.getValue());
				VariableBean check = variableDAO.findByPrimaryKey(
						bean.getProcessID(), bean.getID());
				if (check == null) {
					bean.setProcessID(pid);
					bean.setUpdateDate(ts);
					bean.setID(UniqueKeyGenerator.generate(VariableBean.ID_PREFIX));
					variableDAO.insert(bean, null);
				} else {
					variableDAO.update(check, bean.getValue(), null, ts);
				}
			}

			//エンジン間接続を利用している場合
			String useInterWorkflow = SystemChecker.getProperty(WorkflowEngineConstants.NAUTICA_INTER_WORKFLOW);
			if (useInterWorkflow != null && useInterWorkflow.equals("enable")) {
				InterWorkflowConnectorImpl iwc =
					(InterWorkflowConnectorImpl)InterWorkflowConnectorImpl.getInstance();
				//利用している場合は必要に応じてリモートのエンジンに通知する
				iwc.processAttributesChangedLocal(pid, (Attribute[])context);
			}
			
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセス変数の設定に失敗しました。
			String E0080 = StringManager.get("E0080");
			throw new WorkflowException(E0080, ex);
		}
	}

	/**
	 * 実行オブジェクトにかかわる属性を設定する。
	 *
	 * @param context 属性
	 * @param ts タイムスタンプ
	 * @throws InvalidDataException 無効な属性の場合 
	 * @throws UpdateNotAllowedException 更新ができない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 */
	void setContext(WfNameValue context, Timestamp ts)
	throws InvalidDataException, UpdateNotAllowedException, WorkflowException
	{
		if (context == null) {
			// プロセス変数が未設定です。
			String E0131 = StringManager.get("E0131");
			throw new InvalidDataException(E0131);
		}

		try {
			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			VariableDAO variableDAO = daoFactory.getVariableDAO();

			if (context instanceof Attribute == false) {
				// プロセス変数として認識できない値が格納されています。
				String E0139 = StringManager.get("E0139");
				throw new InvalidDataException(E0139);
			}
			Attribute attr = (Attribute)context;
			VariableBean bean = new VariableBean();
			bean.setProcessID(process.getID());
			bean.setName(attr.getName());
			bean.setType(attr.getType());
			bean.setValue(attr.getValue());
			VariableBean check = variableDAO.findByName(
					bean.getProcessID(), bean.getName());
			if (check == null) {
				bean.setUpdateDate(ts);
				bean.setID(UniqueKeyGenerator.generate(VariableBean.ID_PREFIX));
				variableDAO.insert(bean, null);
			} else {
				variableDAO.update(check, bean.getValue(), null, ts);
			}
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセス変数の設定に失敗しました。
			String E0080 = StringManager.get("E0080");
			throw new WorkflowException(E0080, ex);
		}
	}

	/**
	 * フィルターで特定されたアクティビティを返す。
	 *
	 * @param filter フィルター
	 * @return アクティビティ
	 * @throws InvalidFilterException 無効なフィルターが設定された場合
	 */
	Activity[] getActivities(Filter filter)
	throws InvalidFilterException
	{
		try {
			
			WorkflowDAOFactory daoFactory = DataAccessManager.getDAOFactory();
			ActivityDAO actDAO = daoFactory.getActivityDAO();

			ActivityBean[] beans = actDAO.findByFilter(process.getID(), filter);
			return beans;
		} catch (InvalidFilterException ex) {
			throw ex;
		} catch (Exception ex) {
			// アクティビティの取得に失敗しました。
			String E0124 = StringManager.get("E0124");
			throw new InvalidFilterException(E0124, ex);
		}
	}

	/**
	 * 保持しているプロセスを返す。
	 *
	 * @return プロセス
	 */
	ProcessBean getBean()
	{
		return (ProcessBean)process;
	}

	/**
	 * 実行中の実行オブジェクトの停止を要求する。
	 * 要求が受け入れられた場合、状態はclosed.terminatedに設定される。
	 *
	 * @param checkChild アクティビティをチャックするかどうか
	 * @throws CannotStopException 停止できない場合
	 * @throws NotRunningException 走行中でない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 */
	void terminate(boolean checkChild)
	throws CannotStopException, NotRunningException, WorkflowException
	{
		try {
			boolean closedAlready = false;
			ProcessState state = (ProcessState)getState();
			
			if (ProcessState.CLOSED_COMPLETED.equals(state)
					|| ProcessState.CLOSED_TERMINATED.equals(state)
					|| ProcessState.CLOSED_ABORTED.equals(state)) {
				closedAlready = true;
			}

			String pid = process.getID();

			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			ProcessDAO procDAO = daoFactory.getProcessDAO();
			Timestamp ts = DataAccessManager.getAccessTime();
			if (closedAlready) {
				//すでにほかのアクティビティにより完了しているため、
				//完了状態は変更せずに日付のみを更新する。
				procDAO.updateDate(getBean(), ts);
			} else {
				procDAO.updateState(getBean(), ProcessState.CLOSED_TERMINATED, ts);

				if (checkChild) {
					ActivityDAO actDAO = daoFactory.getActivityDAO();
					ActivityBean[] acts = actDAO.findOpeningActivityByProcessID(pid);

					//管理下のアクティビティをabortする。
					for (int i = 0; i < acts.length; i++) {
						String aid = acts[i].getActivityID();
						ActivityInternal act = (ActivityInternal)getStep(aid);
						act.abort();
					}
				}
			}
		
			ProcessWaitActivityDAO pwDAO = daoFactory.getProcessWaitActivityDAO();
			ProcessWaitActivityBean pwbean = pwDAO.findBySubprocessID(pid);
			
			//待ち合わせている親アクティビティがある場合、完了要求を出す。
			if (pwbean != null && closedAlready == false) {
				String rpid = pwbean.getProcID();
				String raid = pwbean.getActID();
				//メッセージキューに完了要求登録
				WorkflowMessage earmsg = new EndActivityRequestMessage(rpid, raid, ProcessState.CLOSED_TERMINATED);
				SystemMessageTransporter mt = new SystemMessageTransporter(EndActivityRequestWatcher.NAME);
				mt.sendMessage(earmsg);
			} else {
				//プロセス停止時、起動者に通知必要だが、ログ出力に留める。
				// プロセスが停止(TERMINATED)しました。
				String I0048 = StringManager.get("I0048") + "(" + pid + ")";
				eLog.info(I0048);
			}
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセスの停止に失敗しました。
			String E0136 = StringManager.get("E0136");
			throw new WorkflowException(E0136, ex);
		}
	}

	/**
	 * 保留された実行オブジェクトの中断を要求する。
	 * 要求が受け入れられた場合、状態はclosed.abortedに設定される。
	 *
	 * @param checkChild アクティビティをチャックするかどうか
	 * @throws CannotStopException 停止できない場合
	 * @throws NotRunningException 走行中でない場合
	 * @throws WorkflowException　ワークフロー関連で例外が発生した場合
	 */
	void abort(boolean checkChild)
	throws CannotStopException, NotRunningException, WorkflowException
	{
		try {
			boolean closedAlready = false;
			ProcessState state = (ProcessState)getState();
			
			if (ProcessState.CLOSED_COMPLETED.equals(state)
					|| ProcessState.CLOSED_TERMINATED.equals(state)
					|| ProcessState.CLOSED_ABORTED.equals(state)) {
				closedAlready = true;
			}

			String pid = process.getID();

			WorkflowDAOFactory daoFactory
				= DataAccessManager.getDAOFactory();
			ProcessDAO procDAO = daoFactory.getProcessDAO();
			Timestamp ts = DataAccessManager.getAccessTime();
			if (closedAlready) {
				//すでにほかのアクティビティにより完了しているため、
				//完了状態は変更せずに日付のみを更新する。
				procDAO.updateDate(getBean(), ts);
			} else {
				procDAO.updateState(getBean(), ProcessState.CLOSED_ABORTED, ts);

				if (checkChild) {
					ActivityDAO actDAO = daoFactory.getActivityDAO();
					ActivityBean[] acts = actDAO.findOpeningActivityByProcessID(pid);

					//管理下のアクティビティをabortする。
					for (int i = 0; i < acts.length; i++) {
						String aid = acts[i].getActivityID();
						ActivityInternal act = (ActivityInternal)getStep(aid);
						act.abort();
					}
				}
			}
			
			ProcessWaitActivityDAO pwDAO = daoFactory.getProcessWaitActivityDAO();
			ProcessWaitActivityBean pwbean = pwDAO.findBySubprocessID(pid);
			
			//待ち合わせている親アクティビティがある場合、完了要求を出す。
			if (pwbean != null && closedAlready == false) {
				String rpid = pwbean.getProcID();
				String raid = pwbean.getActID();
				//メッセージキューに完了要求登録
				WorkflowMessage earmsg = new EndActivityRequestMessage(rpid, raid, ProcessState.CLOSED_ABORTED);
				SystemMessageTransporter mt = new SystemMessageTransporter(EndActivityRequestWatcher.NAME);
				mt.sendMessage(earmsg);
			} else if (closedAlready) {
				//すでにプロセスがクローズしているので何もしない。
			} else {
				//プロセス中止時、起動者に通知必要だが、ログ出力に留める。
				// プロセスが中止(ABORTED)しました。
				String I0049 = StringManager.get("I0049") + "(" + pid + ")";
				eLog.info(I0049);
			}
		} catch (WorkflowException ex) {
			throw ex;
		} catch (Exception ex) {
			// プロセスの中止に失敗しました。
			String E0137 = StringManager.get("E0137");
			throw new WorkflowException(E0137, ex);
		}
	}

	/**
	 * 開始アクティビティ定義を検索する。
	 *
	 * @param pd プロセス定義ID
	 * @return 開始アクティビティ定義
	 * @throws Exception 任意の例外が起こった場合
	 */
	private ActivityDefinition findStartActivity(ProcessDefinition pd)
	throws Exception
	{
		List<ActivityDefinition> acts = pd.getActivities();
		ActivityDefinition ad = null;
		for (ActivityDefinition def : acts) {
			if (def.getType().equals(ActivityDefinition.START)) {
				ad = def;
				break;
			}
		}
		
		if (ad == null) {
			// プロセス定義内に開始アクティビティがありません。
			String E0140 = StringManager.get("E0140");
			throw new WorkflowException(E0140);
		}
		
		return ad;
	}

	/**
	 * プロセス内に引数で指定されたアクティビティを生成する。
	 *
	 * @param pd プロセス定義
	 * @param ad アクティビティ定義
	 * @param pbean プロセス
	 * @return アクティビティ
	 * @throws Exception 任意の例外が起こった場合
	 */
	private ActivityBean createActivity(
			ProcessDefinition pd, ActivityDefinition ad, ProcessBean pbean)
	throws Exception
	{
		//開始アクティビティの登録
		Timestamp ts = DataAccessManager.getAccessTime();
		int du = pd.getDurationUnit();
		int limit = ad.getLimit();
		long durationLimit = ts.getTime() + DurationHelper.calculateDuration(du, limit);
		Timestamp limitTs = new Timestamp(durationLimit);
		
		ActivityBean abean = new ActivityBean();
		abean.setActivityDefinitionID(ad.getID());
		abean.setActivityID(UniqueKeyGenerator.generate(ActivityBean.ID_PREFIX));
		abean.setActivityState(ActivityState.OPEN_NOT_RUNNING_NOT_STARTED.toInt());
		abean.setActorID(ParticipantImpl.SYSTEM_ID);
		abean.setDurationLimit(limitTs);
		abean.setProcessID(pbean.getID());
		abean.setStartDate(ts);
		abean.setStarterID(pbean.getStarterID());
		abean.setUpdateDate(ts);
		
		WorkflowDAOFactory daoFactory = DataAccessManager.getDAOFactory();
		ActivityDAO actDAO = daoFactory.getActivityDAO();
		actDAO.insert(abean);
		
		return abean;
	}
}
