/* $Id: SystemCommandMessageAdapter.java,v 1.3 2007/11/28 07:09:06 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.ta;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.io.File;
import java.io.Serializable;

import jp.co.argo21.nautica.workflow.dataaccess.AppExecutionBean;
import jp.co.argo21.nautica.workflow.engine.DataAccessManager;
import jp.co.argo21.nautica.workflow.engine.SystemChecker;
import jp.co.argo21.nautica.workflow.jms.WorkflowMessage;
import jp.co.argo21.nautica.workflow.util.StringManager;
import jp.co.argo21.nautica.workflow.wfmc.Attribute;

/**
 * システムコマンド実行用クラス
 * 
 * @author kiyoda(Argo 21, Corp.)
 * @version $Revision: 1.3 $
 * @since Nautica Workflow 1.0
 */
class SystemCommandMessageAdapter extends AbstractToolAgentMessageAdapter
{

	/**
	 * デフォルトコンストラクタ
	 */
	SystemCommandMessageAdapter()
	{
		super();
	}

	@Override
	protected WorkflowMessage getMessage(HashMap<String, Serializable> map)
	{
		return new SystemCommandExecutionInfo(map);
	}

	/**
	 * システムコマンドを実行する。
	 * 
	 * アプリケーション実行情報に従って、システムコマンドを実行する。 実行状況に合わせて、随時、アプリケーション実行テーブル(APPEXEC)を
	 * 更新する。
	 * 
	 * @param mid
	 *            メッセージID
	 * @param message
	 *            メッセージ
	 * @param isRedelivered
	 *            再配信フラグ
	 * @see jp.co.argo21.nautica.workflow.jms.WorkflowMessageAdapter#onMessage(java.lang.String,
	 *      jp.co.argo21.nautica.workflow.jms.WorkflowMessage, boolean)
	 */
	public void onMessage(String mid, WorkflowMessage message,
			boolean isRedelivered)
	{
		if (!SystemCommandExecutionInfo.class.isAssignableFrom(message
				.getClass())) {
			return;
		}
		SystemCommandExecutionInfo info = (SystemCommandExecutionInfo) message;

		if (isRedelivered) {
			//再配信されたメッセージの場合
			ExecutionCounter.update(mid);
		} else {
			//初めて受信したメッセージの場合
			ExecutionCounter.set(mid, info.getRetryCount());
		}

		AppExecutionBean bean = null;
		String command = null;
		Process process = null;
		AbstractToolAgent agent = null;
		ApplicationState astate = ApplicationState.NOT_STARTED;
		try {
			// システムコマンドの設定情報を取得する
			SystemCommandConfig config = SystemCommandConfigFactory.getConfig();
			agent = (AbstractToolAgent) (new SystemCommandToolAgentFactory())
					.getToolAgent();

			// コマンド文字列の取得
			command = getCommandPath(config.getCommandString(info.getAppName()));
			if (command == null) {
				// ログ出力
				String errMsg = StringManager.get("E2008")
						+ "(ApplicationExecutionInfomation=" + info + ")";
				log.error(errMsg);
				return;
			}
			log.info(StringManager.get("I2007")
					+ "(ApplicationExecutionInfomation=" + info + ")");

			// 実行情報から実行するコマンドと引数を含む配列を作成する
			List<String> list = new ArrayList<String>();
			list.add(command);
			Attribute[] attrList = info.getAttributes();
			for (int i = 0; i < attrList.length; i++) {
				list.add(attrList[i].getValue());
			}
			String[] cmdarray = (String[]) list.toArray(new String[] {});

			DataAccessManager.begin(false);
			bean = getApplicationState(info);
			astate = ApplicationState.RUNNING;
			updateState(info, bean.getAppState(), astate);
			// コマンドを実行する
			process = Runtime.getRuntime().exec(cmdarray);

			// Processオブジェクトを、SystemCommandToolAgentに設定する
			SystemCommandToolAgent.setProcess(info.getHandler(), process);

			// アプリケーションの実行が完了するまでスレッドをブロックする
			int result = process.waitFor();
			// ログ
			log.info(StringManager.get("I2008"));

			// アプリケーションの実行結果に応じて、
			// アプリケーション実行テーブルと作業項目を更新する。
			finalizeApplication(info, astate, result, agent);

			ExecutionCounter.clear(mid);
			
			DataAccessManager.commit();
		} catch (Exception ex) {
			// アプリケーション実行に失敗した場合
			int count = ExecutionCounter.get(mid);

			// ログ出力
			String errMsg = StringManager.get("E2011")
				+ ": (Retry Count = " + (count - 1);
			log.error(errMsg, ex);

			if (count > 1) {
				// まだリトライ回数が残っている場合
				try {
					DataAccessManager.rollback();
				} catch (Exception ex2) {
					/* Ignore */
				}
				throw new RuntimeException(ex);
			} else {
				try {
					// アプリケーション実行テーブル(APPEXEC)テーブルの
					// アプリケーションの状態を変更する
					finalizeApplication(info, astate, -1, agent);

					// コミット
					DataAccessManager.commit();
				} catch (Exception ex2) {
					// ログ出力
					String errMsg2 = StringManager.get("E2012");
					log.error(errMsg2, ex);
				}	
			}
		} finally {
			try {
				DataAccessManager.close();
			} catch (Exception ex2) {
				/* Ignore */
			}
		}
	}

	/**
	 * コマンドのパスを返す。
	 * 
	 * @param commandName
	 *            コマンド名
	 * @return コマンドファイルのパス
	 */
	private String getCommandPath(String commandName)
	{

		String separator = SystemChecker.getProperty("file.separator");
		File file = new File(commandName);

		// 絶対パスならそのまま返す。
		if (file.isAbsolute())
			return commandName;

		// それ以外は、頭にローカルパスを補完して返す。
		return SystemCommandToolAgent.LOCAL_COMMAND_PATH + separator
				+ commandName;
	}
}
