/*
 * MosP - Mind Open Source Project    http://www.mosp.jp/
 * Copyright (C) MIND Co., Ltd.       http://www.e-mind.co.jp/
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package jp.mosp.common.utils;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import jp.mosp.common.CommonConst;
import jp.mosp.common.common.BaseDao;
import jp.mosp.common.common.MospException;
import jp.mosp.common.dto.CmAspUserDto;
import jp.mosp.common.dto.CmUserDto;

/**
 * テーブルロックユーティリティ
 * @author yoshida
 *	@version	0.0.1
 */
public class TableLockUtil {
	
	private static final String	FLD_TABLE	= "TABLE";
	
	private LockTableDao		tableLock;
	private List<String>		tableList;
	

	/**
	 * コンストラクタ
	 */
	public TableLockUtil() {
		
	}
	
	/**
	 * コンストラクタ(初期化)
	 * @param cfg	MosPアプリケーション設定(/WEB-INF/mosp.properties)。
	 * @param cmd	リクエストされたCommand。
	 * @param aspUser	ログインASPユーザー。
	 * @param user	ログインユーザー。
	 * @param conn 	DBコネクション。
	 */
	public TableLockUtil(Properties cfg, String cmd, CmAspUserDto aspUser, CmUserDto user, Connection conn) {
		init(cfg, cmd, aspUser, user, conn);
		setTableList(getTableInitList());
	}
	
	/**
	 * 初期化
	 * @param cfg	MosPアプリケーション設定(/WEB-INF/mosp.properties)。
	 * @param cmd	リクエストされたCommand。
	 * @param aspUser	ログインASPユーザー。
	 * @param user	ログインユーザー。
	 * @param conn 	DBコネクション。
	 */
	public void init(Properties cfg, String cmd, CmAspUserDto aspUser, CmUserDto user, Connection conn) {
		tableLock = new LockTableDao();
		tableLock.initDao(cfg, cmd, aspUser, user, conn);
	}
	
	/**
	 * テーブルロック(WRITE)
	 * @throws MospException MosP例外処理が発生した場合
	 * @throws SQLException SQL例外が発生した場合
	 */
	public void lockTables() throws SQLException, MospException {
		tableLock.lockTables(tableList);
	}
	
	/**
	 * テーブルロック
	 * @param tableList		対象テーブル名一覧
	 * @throws MospException MosP例外処理が発生した場合
	 * @throws SQLException SQL例外が発生した場合
	 */
	public void lockTables(List<String> tableList) throws SQLException, MospException {
		setTableList(tableList);
		lockTables();
	}
	
	/**
	 * テーブルロック解除
	 * @throws MospException MosP例外処理が発生した場合
	 * @throws SQLException SQL例外が発生した場合
	 */
	public void unLockTables() throws SQLException, MospException {
		tableLock.unLockTables();
	}
	
	/**
	 * 対象テーブル名一覧初期化
	 * @return	初期テーブル名一覧リスト
	 */
	public List<String> getTableInitList() {
		return new ArrayList<String>();
	}
	
	/**
	 * 対象テーブル名一覧設定
	 * @param tableList 対象テーブル名一覧
	 */
	public void setTableList(List<String> tableList) {
		this.tableList = tableList;
	}
	
	/**
	 * 対象テーブルDao追加
	 * @param dao	対象DAO
	 * @param isWrite 	書き込みロックフラグ
	 */
	public void addTargetTable(BaseDao dao, boolean isWrite) {
		if (VariousUtil.isNull(tableList)) {
			tableList = getTableInitList();
		}
		StringBuffer sb = new StringBuffer();
		sb.append(getTableName(dao));
		sb.append(CommonConst.STR_UNDER_SEPARATOR);
		sb.append(isWrite);
		tableList.add(sb.toString());
	}
	
	/**
	 * 対象テーブルDao追加
	 * @param tableName 	対象テーブル名
	 * @param isWrite 		書き込みロックフラグ
	 */
	public void addTargetTable(String tableName, boolean isWrite) {
		if (VariousUtil.isNull(tableList)) {
			tableList = getTableInitList();
		}
		StringBuffer sb = new StringBuffer();
		sb.append(tableName);
		sb.append(CommonConst.STR_UNDER_SEPARATOR);
		sb.append(isWrite);
		tableList.add(sb.toString());
	}
	
	/**
	 * 対象テーブル名取得
	 * @param dao		対象DAO
	 * @return	対象テーブル名
	 */
	public String getTableName(BaseDao dao) {
		return getTableName(dao, FLD_TABLE);
	}
	
	/**
	 * 対象テーブル名取得
	 * @param dao		対象DAO
	 * @param fieldName		テーブルフィールド名（TABLE）
	 * @return	対象テーブル名
	 */
	public String getTableName(BaseDao dao, String fieldName) {
		String tableName = "";
		try {
			tableName = String.valueOf(dao.getClass().getField(fieldName).get(null));
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}
		return tableName;
	}
	

	/**
	 * テーブルロック用DAO
	 * @author yoshida
	 * @version 0.0.2
	 */
	private static class LockTableDao extends BaseDao {
		
		/**
		 * コンストラクタ
		 */
		public LockTableDao() {
			super();
		}
		
		/**
		 * テーブルロック
		 * @param tableList			対象テーブル情報一覧
		 * @throws SQLException SQL例外が発生した場合
		 * @throws MospException  MosP例外処理が発生した場合
		 */
		public void lockTables(List<String> tableList) throws SQLException, MospException {
			try {
				prepareStatement(getLockTableSQL(tableList));
				executeUpdate();
			} catch (SQLException e) {
				throw e;
			} finally {
				releaseResultSet();
				releasePreparedStatement();
			}
		}
		
		/**
		 * テーブルロック解除
		 * @throws SQLException SQL例外が発生した場合
		 * @throws MospException  MosP例外処理が発生した場合
		 */
		public void unLockTables() throws SQLException, MospException {
			if (getRdbmsType() == 1) {
				try {
					prepareStatement("UNLOCK TABLES ");
					executeUpdate();
				} catch (SQLException e) {
					throw e;
				} finally {
					releaseResultSet();
					releasePreparedStatement();
				}
			}
		}
		
		/**
		 * テーブルロック用SQL生成
		 * @param tableList		対象テーブル情報一覧
		 * @return	テーブルロック用SQL
		 * @throws MospException MosP例外処理が発生した場合
		 */
		private String getLockTableSQL(List<String> tableList) throws MospException {
			StringBuffer sb = new StringBuffer();
			switch (getRdbmsType()) {
				case 1:
					// MySQLの場合
					sb.append("LOCK TABLES ");
					for (String table : tableList) {
						String[] code = table.split(CommonConst.STR_UNDER_SEPARATOR);
						sb.append(code[0]);
						if (Boolean.parseBoolean(code[1])) {
							sb.append(" WRITE ");
						} else {
							sb.append(" READ ");
						}
						sb.append(CommonConst.STR_UNDER_SEPARATOR);
					}
					sb.delete(sb.lastIndexOf(CommonConst.STR_UNDER_SEPARATOR), sb.length());
					break;
				case 2:
					// PostgreSQLの場合
					sb.append("LOCK TABLE ");
					for (String table : tableList) {
						String[] code = table.split(CommonConst.STR_UNDER_SEPARATOR);
						if (Boolean.parseBoolean(code[1])) {
							sb.append(code[0]);
							sb.append(CommonConst.STR_UNDER_SEPARATOR);
						}
					}
					sb.delete(sb.lastIndexOf(CommonConst.STR_UNDER_SEPARATOR), sb.length());
					break;
				default:
					break;
			}
			return sb.toString();
		}
		
	}
	
}
