/*********************************************************************
*	All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*	システム名		：eo顧客基幹システム
*	モジュール名	：JZMejbZM0501KRCK
*	ソースファイル名：JZMejbZM0501KRCK.java
*	作成者			：富士通
*	日付			：2012年06月17日
*＜機能概要＞
*	量販訪販ユーザーパスワード制約部品クラス
*＜修正履歴＞
*	バージョン	修正日		修正者		修正内容
*	v4.00		2012/06/17	FJ）早崎	新規作成
*
**********************************************************************/

package eo.ejb.common.entity;

import java.lang.reflect.Method;
import com.fujitsu.futurity.model.base.CAANFinderException;
import com.fujitsu.futurity.model.base.CAANMsg;
import com.fujitsu.futurity.model.base.CAANRuntimeException;
import com.fujitsu.futurity.model.ejb.common.fw.AgentDispatchContext;

import com.fujitsu.futurity.model.ejb.common.fw.TemplateSQLEntity;

/**
 * <p>
 * 量販訪販ユーザーパスワード制約部品クラスです。
 * </p>
 * @author 富士通
 */
public  class JZMejbZM0501KRCK extends TemplateSQLEntity
{
	/** スキーマ名 */
	private String schemaName = null;

	/** スキーマ定義情報 */
	private Object[][] contents = null;

	/** テーブル名 */
	private String tableName = null;

	/** スキーマ定義情報の取得メソッド名 */
	private static final String GET_CONTENTS = "getContents";

	/** スキーマテーブル名の取得メソッド名 */
	private static final String GET_TABLE = "getTableName";

	/** 更新年月日時分秒項目名(英名) */
	private static final String UPD_DTM = "UPD_DTM";

	/** パッケージ名 */
	private static final String PACKAGE_NAME = "eo.ejb.cbm.entity.";

	/** ETメッセージ接尾辞 */
	private static final String SUFFIX_ETMSG = "ETMsg";

	/** LogigalEntity接尾辞 */
	private static final String SUFFIX_LE = "LE";

	
	/** 項目名(パスワード設定年月日時分秒) */
	private static final String PWD_SETTE_DTM = "PWD_SETTE_DTM";

	/** メソッド名(findByCondition) */
	private static final String FIND_BY_CONDITION = "findByCondition";
	

	/**
	 * コンストラクタです。
	 */
	public JZMejbZM0501KRCK()
	{
	}

	/**
	 * 新しいJZMejbZM0501KRCKを作成します。
	 * <br>
	 * @param arg0 スキーマ名
	 */
	public JZMejbZM0501KRCK(String arg0)
	{
		super();
		setSchemaInfo(arg0);
	}

	/**
	 * スキーマ名を取得します。
	 * <br>
	 * @return スキーマ名
	 */
	protected String getSchemaName()
	{
		return this.schemaName;
	}

	/**
	 * スキーマの定義情報を取得します。
	 * <br>
	 * @return スキーマの定義情報
	 */
	protected Object[][] getSchemaContents()
	{
		return this.contents;
	}

	/**
	 * スキーマのテーブル名を取得します。
	 * <br>
	 * @return スキーマのテーブル名
	 */
	protected String getTableName()
	{
		return this.tableName;
	}

	/**
	 * スキーマ情報を設定します。
	 * パラメータのスキーマ名から対象スキーマの定義情報、テーブル名を設定します。
	 * <br>
	 * @param arg0 スキーマ名
	 */
	private void setSchemaInfo(String arg0)
	{
		this.schemaName = arg0;
		setSchemaContents();
		setTableName();
	}

	/**
	 * スキーマの定義情報を設定します。
	 * <br>
	 */
	private void setSchemaContents()
	{
		try
		{
			Class<?> cls = Class.forName(this.schemaName);
			Object instance = cls.newInstance();
			Method method = cls.getMethod(GET_CONTENTS, null);
			this.contents = (Object[][])method.invoke(instance, null);
		}
		catch (Exception e)
		{
			throw new CAANRuntimeException(e);
		}
	}

	/**
	 * スキーマのテーブル名を設定します。
	 * <br>
	 */
	private void setTableName()
	{
		try
		{
			Class<?> cls = Class.forName(this.schemaName);
			Object instance = cls.newInstance();
			Method method = cls.getMethod(GET_TABLE, null);
			this.tableName = (String)method.invoke(instance, null);
		}
		catch (Exception e)
		{
			throw new CAANRuntimeException(e);
		}
	}

	/**
	 * 履歴管理スキーマに対する排他モードタイムスタンプチェックを行います。
	 * <br>
	 * @param inMsg 処理対象のメッセージキャリア
	 * @param inContext Agentから渡されたAgentDispatchContext
	 * @param schemaID スキーマID
	 * @return 排他処理が成功した場合はtrue、成功しなかった場合はfalse
	 */
	public static boolean succeededExclusiveLockForGene(CAANMsg inMsg, AgentDispatchContext inContext, Object schemaID)
	{
		// 履歴管理スキーマに対する排他モードタイムスタンプチェックを行う
		JZMejbZM0501KRCK ins = new JZMejbZM0501KRCK(PACKAGE_NAME + schemaID + SUFFIX_ETMSG);
		return ins.privateSucceededExclusiveLockForGene(inMsg, (String)schemaID);
	}

	/**
	 * 履歴管理スキーマに対する排他モードタイムスタンプチェックを行います。
	 * <br>
	 * @param inMsg 処理対象のメッセージキャリア
	 * @param schemaID スキーマID
	 * @return 排他処理が成功した場合はtrue、成功しなかった場合はfalse
	 */
	private boolean privateSucceededExclusiveLockForGene(CAANMsg inMsg, String schemaID)
	{
		// メッセージキャリアのnullチェック
		chkMsg(inMsg);
		
		CAANMsg[] msg = findByPKWithoutGeneCol(inMsg, schemaID);
		
		if (0 == msg.length)
		{
			return false;
		}
		
		// タイムスタンプチェック
		// inMsgの更新前更新年月日時分秒が最新でない場合、falseを返却
		if (!isSameUpdDtm(inMsg, msg))
		{
			return false;
		}

		// ロックをかけるレコード(更新年月日時分秒が最大のレコード)の抽出
		CAANMsg latestRec = null;
		for (int i = 0; i < msg.length; i++)
		{
			boolean latestFlg = true;
			for (int j = 0; j < msg.length; j++)
			{
				if (0 > msg[i].getString(UPD_DTM).compareTo(msg[j].getString(UPD_DTM)))
				{
					latestFlg = false;
					break;
				}
			}
			if (latestFlg)
			{
				latestRec = msg[i];
				break;
			}
		}
		
		// 抽出したレコードをロック
		try
		{
			findByKeyForUpdate(latestRec);
			return true;
		}
		catch (CAANFinderException cfe)
		{
			// ロック対象のレコードが存在しない場合
			return false;
		}
		catch (CAANRuntimeException cre)
		{
			// 既に排他ロックがかかっていた場合
			return false;
		}
	}

	/**
	 * エンティティのプライマリキーを格納したメッセージを取得します。<br>
	 * プライマリキーに履歴管理カラム名が存在する場合は、メッセージには含めません。
	 * <br>
	 * @param inMsg エンティティの情報を格納したメッセージキャリア
	 * @param keyList プライマリキー項目のスキーマ項目名とDBカラム名のリスト
	 * @param schemaID スキーマのID
	 * @return 履歴管理カラムを除いたプライマリキーを格納したメッセージ
	 */
	private CAANMsg getPKMsg(CAANMsg inMsg, String[][] keyList, String schemaID)
	{
		CAANMsg msg = new CAANMsg(PACKAGE_NAME + schemaID + SUFFIX_ETMSG);

		for (int i = 0; i < keyList[0].length; i++)
		{
			if (keyList[0][i].equals(PWD_SETTE_DTM))
			{
				// パスワード設定年月日時分秒を除く
				continue;
			}

			if (inMsg.containsKeyOfMsgData(keyList[0][i]))
			{
				msg.set(keyList[0][i], inMsg.getString(keyList[0][i]));
			}
		}

		return msg;
	}

	/**
	 * 履歴管理カラムをのぞいたプライマリキー検索を行います。
	 * <br>
	 * @param inMsg 処理対象のメッセージキャリア
	 * @param schemaID スキーマID
	 * @return 履歴管理カラムをのぞいたプライマリキー検索結果
	 */
	private CAANMsg[] findByPKWithoutGeneCol(CAANMsg inMsg, String schemaID)
	{
		CAANMsg[] outMsg = null;

		try
		{
			String[][] keyList = getKeyColumnList();
			CAANMsg msg = getPKMsg(inMsg, keyList, schemaID);

			Class<?> cls = Class.forName(PACKAGE_NAME + schemaID + SUFFIX_LE);
			Object obj = cls.newInstance();
			Method method = cls.getMethod(FIND_BY_CONDITION, CAANMsg.class);
			outMsg = (CAANMsg[])method.invoke(obj, msg);
		}
		catch (Exception e)
		{
			throw new CAANRuntimeException(e);
		}
		
		return outMsg;
	}
	
	/**
	 * 更新年月日時分秒を比較します。
	 * <br>
	 * @param inMsg 処理対象のメッセージキャリア
	 * @param compMsg[] 履歴管理カラム以外のPKで取得したレコード(n件)
	 * @return inMsgの更新年月日時分秒とcompMsgの最大の更新年月日時分秒が等しい場合はtrue、等しくない場合はfalse
	 */
	private boolean isSameUpdDtm(CAANMsg inMsg, CAANMsg[] compMsg)
	{
		if (null == inMsg)
		{
			throw new IllegalArgumentException("inMsgの値が不正です。");
		}
		
		if (null == compMsg || 0 == compMsg.length)
		{
			throw new IllegalArgumentException("compMsgの値が不正です。");
		}
		
		// 最新の更新年月日時分秒を取得
		String latestUpdDtm = null;
		for (int i = 0; i < compMsg.length; i++)
		{
			boolean latestFlg = true;
			for (int j = 0; j < compMsg.length; j++)
			{
				if (0 > compMsg[i].getString(UPD_DTM).compareTo(compMsg[j].getString(UPD_DTM)))
				{
					latestFlg = false;
					break;
				}
			}
			if (latestFlg)
			{
				latestUpdDtm = compMsg[i].getString(UPD_DTM);
				break;
			}
		}

		if (!(inMsg.getString(UPD_DTM).equals(latestUpdDtm)))
		{
			// 最新の更新年月日時分秒とinMsgの更新年月日時分秒が異なる場合
			return false;
		}

		return true;
	}

	/**
	 * メッセージキャリアのnullチェックを行います。
	 * <br>
	 * @param inMsg メッセージキャリア
	 */
	private void chkMsg(CAANMsg inMsg)
	{
		if (null == inMsg)
		{
			throw new IllegalArgumentException("inMsgの値が不正です。");
		}
	}
	
	

}
