/*
 * Copyright (C) 2005 NTT DATA Corporation
 * 
 */
package org.postgresforest.vm.gsc;

import java.net.UnknownHostException;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.StringTokenizer;

import org.postgresforest.Driver;
import org.postgresforest.jdbc2.AbstractJdbc2Connection;
import org.postgresforest.util.GT;
import org.postgresforest.util.PSQLException;
import org.postgresforest.vm.Hash01;
import org.postgresforest.vm.HashLoader;
import org.postgresforest.vm.Hash_I;
import org.postgresforest.vm.LogUtil;
import org.postgresforest.vm.Logger;
import org.postgresforest.vm.ParamValue;
import org.postgresforest.vm.PartitionColumnInfo;
import org.postgresforest.vm.err.BrokenNotify_I;
import org.postgresforest.vm.err.DistError;
import org.postgresforest.vm.err.ForestSQLState;
import org.postgresforest.vm.err.NoBrokenNotifier;
import org.postgresforest.vm.jdbc.ForestConnection;

/**
 * グローバルシステムカタログクラス.
 * 
 * コンフィグレーションファイルの情報と、グローバルシステムカタログの情報、
 * およびコードへのコネクションリストを保持し、パーティション化、分散処理に
 * 必要な情報とコネクションを提供する。
 * スレッドでグローバルシステムカタログの更新を行う。
 * 
 */
public class GscData extends Thread {

	protected ConfigInfo m_configInfo = null; //コンフィグレーション情報
	
	protected ArrayList m_servers = null; //サーバ情報リスト

	protected HashMap m_tables = null; //テーブル情報リスト


	protected LogUtil m_logUtil; 	// ログ出力 @since 2.1
	protected static String m_client; 	// ログ出力 @since 2.1

	protected boolean m_tableAvailable = false; //Ver3.1オンラインメンテナンス

	
	public static final int KEY_TYPE_NAME = 0;
	public static final int KEY_TYPE_NO = 1;

	//Date,Time、TimeStampの書式定義。対応する書式を増やすときは、配列にフォーマットを追加する
	protected static final DateFormat m_formatDate[] = { SimpleDateFormat.getDateInstance(DateFormat.LONG) };
	protected static final DateFormat m_formatTime[] = { SimpleDateFormat.getTimeInstance(DateFormat.LONG) };
	protected static final DateFormat m_formatTimeStamp[] = { SimpleDateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG) };


	protected String m_dbName = null; //DB名
	protected Properties m_prop = null; //ユーザ・パスワード

	protected int m_DistSvrIdx = 0; //分散処理用サーバインデックス

	protected boolean m_isClosed = false; //Close判定//Ver1.1

	protected boolean m_hasPartition = false; //パーティション化テーブル有無//Ver2.1


	protected static BrokenNotify_I m_brokenNotifier; //更新通知//Ver3.1

	
	protected SQLException m_thredErr = null;	//スレッド実効エラーを格納


	protected ArrayList m_gscInfoList;

	
	static {
		//自局のアドレス取得
		try {
			m_client = java.net.InetAddress.getLocalHost().toString();
		} catch (UnknownHostException e1) {
			m_client = "UnknownHost";
		}
		
		//外部ステータス監視ツールのロードVer3.1
		try {
            m_brokenNotifier = (BrokenNotify_I)Class.forName("org.postgresforest.vm.BrokenNotifier").newInstance();
        } catch (Exception e) {
            //ロードできないときは何もしない通知クラスを使用する。
            m_brokenNotifier = new NoBrokenNotifier();
        }

		
	}

	/**
	 *　コンストラクタ.
	 *　プロパティ
	 * 
	 * @param configFile
	 * @param dbname
	 * @param info
	 * @param driver
	 * @throws SQLException
	 */	
public GscData(
		String gsc,
		String config,
		String dbname,
		Properties info,
		LogUtil logUtil) throws SQLException{


		m_logUtil = logUtil;
		Logger.setLogUtil(m_logUtil);


		//メンバ変数に格納
		m_dbName = dbname;
		m_prop = info;

		//configIDの取得
		//コンフィグIDが指定されていない場合、デフォルトのコンフィグ情報を読み込む
		if(config == null){
			m_configInfo = new ConfigInfo("FOREST_DEFAULT_CONFIG");
		}else{
			m_configInfo = new ConfigInfo(config);
		}

		//コンフィグファイルを読み込み
		initialGscList(gsc);

		//グローバルシステムカタログの読み込み
		readData();
		
		if(m_configInfo.getReflesh() !=  0){
			//キャッシュ更新スレッド起動（接続時のみ：いったん停止した場合はコンフィグ読みにいかないから。）
			setDaemon(true);
			start();
		}


	}
	/**
	 * 処理対象となり得る（ユーザDBを保持している）全サーバへの接続を行う。
	 *
	 * 接続に失敗した場合には、サーバ情報のステータスを `障害中` として
	 * GSC内のサーバステータスを更新する。
	 */
	protected void connectServers() {
		for (int i = 0; i < m_servers.size(); i++) {
			ServerInfo srvinf = (ServerInfo) m_servers.get(i);
		
			if (srvinf.getStatus() == ServerInfo.SERVER_RUNNING && 
			    srvinf.getConnection() == null         ) {
		
				String url = srvinf.getUrl() + m_dbName;
		
				try {
					Connection con = connect(url,m_prop);
					srvinf.setConnection(con);

					Logger.info("It connected with the DB server. (" + url + ")");
				} catch (SQLException e2) {
					//ステータスに故障中をセット
					srvinf.setStatus(ServerInfo.SERVER_TROUBLE);
					//グローバルシステムカタログ更新
					updateDBstatus(srvinf, "CONNECT", e2 );

					Logger.info("It was not connectable with the DB server. (" + url + ")");
				}
			}
		
		}
	}
	
	private static final String PROTOCOL = "jdbc:postgresforest:";

    public java.sql.Connection connect(String url, Properties info) throws SQLException{
    	return DriverManager.getConnection(PROTOCOL + url,info);
   	
	}



	/**
	 * サーバの状態を `障害中` に設定する。
	 *
	 * サーバリストの中からサーバ情報ServerInfoを探し、ステータスを `障害中` に変更、
	 * GSCのサーバステータスを更新し、BrokenLogを記録、障害サーバへのコネクションを閉じる。
	 *
	 * @param Connection con 障害を検出したサーバへのConnectionオブジェクト
	 * @param String query 障害発生時に実行していたSQL文
	 * @param Exception ex 発生した例外
	 */
	synchronized public void setServerBroken(Connection con, String query, Exception ex)
		throws SQLException {

		//クローズ後、サーバ切り離しは行わない
		if(isClosed()){
			return;
		}
		

		//コネクションリストの更新

		for (int i = 0; i < m_servers.size(); i++) {
			ServerInfo srvinf = (ServerInfo) m_servers.get(i);

			if (con.equals(srvinf.getConnection())
				&& srvinf.getStatus() != ServerInfo.SERVER_TROUBLE) {

				//ステータス更新
				srvinf.setStatus(ServerInfo.SERVER_TROUBLE);
				
				//グローバルシステムカタログの更新
				updateDBstatus(srvinf, query, ex);
				
				
				Logger.info("Server status was set up into the obstacle. (" + srvinf.getUrl() + ")");

				con.close();

				//Ver3.1 オンラインメンテ／デバッグ　閉じたコネクションを保持したため、障害復旧時に新しくコネクトしていないので、
				//コネクションクリア
				srvinf.setConnection(null);
				
				break;
			}

		}

	}



	/**
	 * 障害判定タイムアウト値(秒)
	 * @return
	 */
	public int getTimeout() {
		return m_configInfo.getTimeout();
	}

	/**
	 * パーティション化されているテーブルか？
	 * @param tableName
	 * @return
	 * @throws SQLException
	 */
	synchronized public boolean isPartition(String tableName) throws SQLException {

		//スレッドエラーチェック
		chkThredErr();

		TableInfo tblinfo = getTableInfo(tableName);

		return tblinfo.isPartition();
	}
	
	/**
	 * テーブル情報の取得
	 * @param tableName
	 * @return
	 * @throws PSQLException
	 */
	protected TableInfo getTableInfo(String tableName) throws PSQLException {
		String tblNameL = tableName.toLowerCase();
		
		TableInfo tblinfo = (TableInfo) m_tables.get(tblNameL);
		if (tblinfo == null) {
			//テーブルが定義されていない
			throw new PSQLException(GT.tr("The table for {0} is not defined as GSC.",tableName), ForestSQLState.INTERNAL_ERROR );
		}
		return tblinfo;
	}

	/**
	 * パーティション数取得
	 * @param tableName
	 * @return
	 */
	synchronized public int getPartCount(String tableName) throws SQLException{

		//スレッドエラーチェック
		chkThredErr();

		TableInfo tblinfo = getTableInfo(tableName);
		
		//Ver2.0
		return tblinfo.getPartitionNum();
	}
	
	/**
	 * パーティション属性名を返す
	 * @param tableName
	 * @return
	 */
	synchronized public ArrayList getPartCol(String tableName) throws SQLException{

		//スレッドエラーチェック
		chkThredErr();

		TableInfo tblinfo = getTableInfo(tableName);		
		
		ArrayList collist = tblinfo.getColumnList();

		return collist;
	}

	/**
	 * パーティション属性名を返す
	 * @param tableName
	 * @return
	 */
	synchronized public String[] getPartColNames(String tableName) throws SQLException{

		//スレッドエラーチェック
		chkThredErr();

		TableInfo tblinfo = getTableInfo(tableName);		
		
		ArrayList collist = tblinfo.getColumnList();
		if(collist == null){
			return new String[0];
		}
		String[] colNames = new String[collist.size()];
		for (int i = 0; i < colNames.length; i++) {
			PartitionColumnInfo colinfo = (PartitionColumnInfo) collist.get(i);
			colNames[i] = colinfo.getName();

		}

		return colNames;
	}

	/**
	 * パーティション属性の項目番号を返す
	 * @param tableName
	 * @return
	 */
	synchronized public int[] getPartColNums(String tableName) throws SQLException{

		//スレッドエラーチェック
		chkThredErr();

		TableInfo tblinfo = getTableInfo(tableName);	
			
		ArrayList collist = tblinfo.getColumnList();
		int[] colNums = new int[collist.size()];
		for (int i = 0; i < colNums.length; i++) {
			PartitionColumnInfo colinfo = (PartitionColumnInfo) collist.get(i);
			colNums[i] = colinfo.getNumber();

		}

		return colNums;
	}

	/**
	 * 分散振り分け処理の接続先取得
	 * <pre>
	 * SELECTで一箇所のサーバで処理させる場合
	 * </pre> 
	 * @param table
	 * @return
	 * @throws SQLException
	 */
	synchronized public Connection getDistServer(String table[]) throws SQLException{

		//スレッドエラーチェック
		chkThredErr();

		//処理可能サーバの抽出
		TableInfo tblinfo = getTableInfo(table[0]);

		ArrayList svrlist = (ArrayList) tblinfo.getServers().clone();
		//処理可能なサーバリスト
		for (int i = 1; i < table.length; i++) {
			TableInfo tblinfoWk = getTableInfo(table[i]);
			ArrayList svrlistWk = tblinfoWk.getServers();

			Iterator iter = svrlist.iterator();
			while (iter.hasNext()) {
				ServerInfo svrInfo = (ServerInfo) iter.next();
				if (!svrlistWk.contains(svrInfo)) {
					iter.remove();
				}
			}
		}

		//接続先の候補のINDEX取得
		int serverIdx = getSvrIdx();

		if(serverIdx < 0){
			//処理できるサーバーがない
			throw new PSQLException(GT.tr("There is no servers which can be processed."),ForestSQLState.INTERNAL_ERROR);
		}

		//接続先の検索
		for (int i = 0; i < m_servers.size(); i++) {

			ServerInfo svrInfo = (ServerInfo) m_servers.get(serverIdx);

			svrInfo = (ServerInfo) m_servers.get(serverIdx);
			//処理可能なサーバリストに含まれている？
			if (svrInfo.getStatus() == ServerInfo.SERVER_RUNNING
				&& svrlist.contains(svrInfo)) {
				//含まれていればコネクションを返す

				return svrInfo.getConnection();
			}
			//含まれていなければ、次のサーバ
			serverIdx++;
			if (m_servers.size() <= serverIdx) {
				serverIdx = 0;
			}

		}
		//処理できるサーバーがない
		throw new PSQLException(GT.tr("There is no servers which can be processed."),ForestSQLState.INTERNAL_ERROR);
	}
 

	/**
	 * SELECTの負荷分散処理で、前回使用したサーバへのConnectionオブジェクトを取得する。
	 *
	 * メタデータへのアクセスなど、Connectionオブジェクトが必要な場合に、
	 * 「次のサーバ」へカウンタを進めることなく「前回使用したConnectionオブジェクト」を
	 * 取得する際に使用する。
	 *
	 * サーバのステータスが `稼働中` ではない場合には、稼動している次のサーバへの
	 * Connectionオブジェクトを返却する。
	 *
	 * @return Connection 前回処理したサーバのConnectionオブジェクト
	 */
	synchronized public Connection getLastServer() throws SQLException {
		//スレッドエラーチェック
		chkThredErr();

		ServerInfo svrInfo = (ServerInfo) m_servers.get(m_DistSvrIdx);

		if (svrInfo.getStatus() == ServerInfo.SERVER_RUNNING)
		{
			return svrInfo.getConnection();
		}
		
		return getDistServer();
	}

	/**
	 * SELECTで負荷分散処理で、「次に」使用するサーバへのConnectionオブジェクトを
	 * 取得する。
	 * 
	 * @return Connection SELECTの負荷分散で使用するサーバへのConnectionオブジェクト
	 */
 	synchronized public Connection getDistServer() throws SQLException{
		//スレッドエラーチェック
		chkThredErr();

		/*
		 * `稼働中`のサーバの中から接続先の候補のINDEXを取得する。
		 */
		int serverIdx = getSvrIdx();

		if(serverIdx < 0){
			//処理できるサーバがない。
			throw new PSQLException(GT.tr("There is no servers which can be processed."),
									ForestSQLState.INTERNAL_ERROR);
		}

		ServerInfo svrInfo = (ServerInfo) m_servers.get(serverIdx);

		return svrInfo.getConnection();
	}

	/**
	 * パーティション化テーブルの接続先取得.
	 * 
	 * @param tableName
	 * @param PartNo
	 * @return
	 * @throws SQLException
	 */
	synchronized public Connection getDistServer(String tableName, int PartNo) throws SQLException{

		//スレッドエラーチェック
		chkThredErr();

		//テーブルからテーブル情報取得
		TableInfo tblinfo = getTableInfo(tableName);


		//サーバーリストを取得
		ArrayList svrList = tblinfo.getServers(PartNo);
		//パーティションに対応した
		for (int i = 0; i < svrList.size(); i++) {
			ServerInfo srvinfo = (ServerInfo) svrList.get(i);
			//サーバが故障チェック 
			if (srvinfo.getStatus() == ServerInfo.SERVER_RUNNING) {
				return srvinfo.getConnection();
			}
		}

		throw new PSQLException(GT.tr("There is no servers which can be processed."), ForestSQLState.INTERNAL_ERROR);


	}

	/**
	 * 並列処理の接続先リスト取得.
	 * INSERT、UPDATE、DELETE処理で処理するサーバリスト
	 * 
	 * @param tableName - 処理対象
	 * @param tableList - 処理対象以外（WHERE句など）のテーブルリスト
	 * @return - コネクション配列
	 * @throws SQLException
	 */
	synchronized public ArrayList getDistServerList(
		String tableName,
		String[] tableList) throws SQLException{

		//スレッドエラーチェック
		chkThredErr();

		//処理対象テーブルが存在するサーバリストを取得		
		TableInfo tblinfo = getTableInfo(tableName);		
		ArrayList srvlist = tblinfo.getServers();

		ArrayList distSvrlist = new ArrayList(); //正常なサーバリスト
		for (int i = 0; i < srvlist.size(); i++) {
			ServerInfo srvinfo = (ServerInfo) srvlist.get(i);
			if (srvinfo.getStatus() == ServerInfo.SERVER_RUNNING) {
				//正常なサーバをリストに追加
				distSvrlist.add(srvinfo);
			}
		}

		//正常なサーバリストのサーバがその他のテーブルを含んでいるかチェック
		for (int i = 0; i < tableList.length; i++) {
			TableInfo tblinfoWk = getTableInfo(tableList[i]);
			ArrayList srvlistWk = tblinfoWk.getServers();
			if (!srvlistWk.containsAll(distSvrlist)) {
				//処理できないサーバが存在する
				throw new PSQLException(GT.tr("The server which cannot be processed exists."), ForestSQLState.INTERNAL_ERROR);
				
			}
		}

		return getDistServerList(tableName);
	}

	/**
	 * 並列処理の接続先リスト取得.
	 * INSERT、UPDATE、DELETE処理で処理するサーバリスト
	 * 
	 * @param tableName
	 * @return - コネクション配列
	 * @throws SQLException
	 */
	synchronized public ArrayList getDistServerList(String tableName) throws SQLException{

		//スレッドエラーチェック
		chkThredErr();

		TableInfo tblinfo = getTableInfo(tableName);

		ArrayList srvlist = tblinfo.getServers();
		ArrayList conlist = new ArrayList();
		for (int i = 0; i < srvlist.size(); i++) {
			ServerInfo srvinfo = (ServerInfo) srvlist.get(i);
			if (srvinfo.getStatus() == ServerInfo.SERVER_RUNNING) {
				conlist.add(srvinfo.getConnection());
			}
		}

		if(conlist.size() == 0){
			//処理できるサーバーがない
			throw new PSQLException(GT.tr("There is no servers which can be processed."),ForestSQLState.INTERNAL_ERROR);
		}

		return conlist;
	}

	/**
	 * 並列処理の接続先リスト取得.
	 * INSERT、UPDATE、DELETE処理で処理するサーバリスト
	 * パーティションを指定し、パーティションが存在するサーバリストを返す
	 * 
	 * @param tableName テーブル名
	 * @param PartNo パーティションNo
	 * @return - コネクション配列
	 * @throws SQLException
	 * @since 3.0
	 */
	synchronized public ArrayList getDistServerList(String tableName,int PartNo) throws SQLException{

		//スレッドエラーチェック
		chkThredErr();

		//Ver3,0
		//TableInfo tblinfo = (TableInfo) m_tables.get(tableName.toLowerCase());
		TableInfo tblinfo = getTableInfo(tableName);		
		if (tblinfo == null) {
			//テーブルが定義されていない
			throw new PSQLException(GT.tr("The table for {0} is not defined as GSC.",tableName), ForestSQLState.INTERNAL_ERROR );
		}
		
		ArrayList srvlist = tblinfo.getServers(PartNo);
		ArrayList conlist = new ArrayList();
		for (int i = 0; i < srvlist.size(); i++) {
			ServerInfo srvinfo = (ServerInfo) srvlist.get(i);
			if (srvinfo.getStatus() == ServerInfo.SERVER_RUNNING) {
				conlist.add(srvinfo.getConnection());
			}
		}

		if(conlist.size() == 0){
			//処理できるサーバーがない
			throw new PSQLException(GT.tr("There is no servers which can be processed."),ForestSQLState.INTERNAL_ERROR);
		}

		return conlist;
	}

	/**
	 * 並列処理の接続先リスト取得.
	 * 接続中のすべてのサーバリスト
	 * 
	 * @return コネクション配列
	 * @throws SQLException
	 */
	synchronized public ArrayList getDistServerList() throws SQLException{
	
		//スレッドエラーチェック
		chkThredErr();


		ArrayList conlist = new ArrayList();
		for (int i = 0; i < m_servers.size(); i++) {
			ServerInfo srvinfo = (ServerInfo) m_servers.get(i);
			if (srvinfo.getStatus() == ServerInfo.SERVER_RUNNING) {
				conlist.add(srvinfo.getConnection());
			}
		}
	
		if(conlist.size() == 0){
			//処理できるサーバーがない
			throw new PSQLException(GT.tr("There is no servers which can be processed."),ForestSQLState.INTERNAL_ERROR);
		}
	
		return conlist;
	}

	/**
	 * ユーザDBを保持しているサーバ数を取得する
	 * 
	 * @return int サーバ数
	 */
	public int getServerCount() {
		return m_servers.size();
	}

	/**
	 * パーティション化テーブルのパーティションNo決定.
	 * @param table	対象テーブル名
	 * @param valueMap KEY：項目名　value；値 のhashテーブル
	 * @param type     valueMapの KEYに設定されている値 KEY_TYPE_NAME(String) or KEY_TYPE_NO(Integer) 
	 * @return			パーティションNo
	 * @throws PSQLException
	 */
	synchronized public int getPartNo(String table, HashMap valueMap, int type)
		throws SQLException {

		//スレッドエラーチェック
		chkThredErr();

		//Ver3.0　ハッシュ関数をテーブル情報から取得
		TableInfo tblinf = getTableInfo(table);
		Hash_I hashobj = tblinf.getHash();

		synchronized (hashobj){

		//論理テーブルと項目名/項目Noからデータ型を取得して
		//hash関数を使用してノードナンバー決定して実テーブル名を生成して返す。

		ArrayList colList = tblinf.getColumnList();

		hashobj.clearCol();
		hashobj.setPartNum(getPartCount(table));

		for (int i = 0; i < colList.size(); i++) {
		    PartitionColumnInfo colinfo = (PartitionColumnInfo) colList.get(i);

			String value = null;
			if (type == KEY_TYPE_NAME) {
				ParamValue paramValue = (ParamValue)valueMap.get(colinfo.getName()); 
				value = paramValue.getValue();
			} else {
				Integer colNum = new Integer(colinfo.getNumber());
				ParamValue paramValue = (ParamValue)valueMap.get(colNum);
				value = paramValue.getValue();
			}

			if ( value == null) {
				//パーティション項目にNULLがセット
				throw new PSQLException(GT.tr("NULL sets to a partition item."), ForestSQLState.INTERNAL_ERROR);
			}

			switch (colinfo.getType()) {

				case Types.SMALLINT :
				case Types.INTEGER :
					//TODO 数値のチェック要る？
					hashobj.addCol( new Double(value).intValue());

					break;

				case Types.CHAR :
				case Types.VARCHAR :
					//Ver2.0 エスケープを統一
					value = value.replaceAll("\\\\n", "\n" );
					value = value.replaceAll("\\\\t", "\t" );
					value = value.replaceAll("\\\\r", "\r" );
					value = value.replaceAll("''", "\\'" );

					hashobj.addCol(value);

					break;

				case Types.DATE:

					try {
						hashobj.addCol(Date.valueOf(value));
					} catch (Exception e) {
						synchronized(m_formatDate){
		
							long timelong = 0;
							for (int j = 0; j < m_formatDate.length; j++) {
								try {
									timelong = m_formatDate[j].parse(value).getTime();
									hashobj.addCol(new Date(timelong));
									break;
								} catch (ParseException e1) {
								}
		
							}								
		
							if(timelong == 0){								
								//パーティション項目が未対応の型
								throw new PSQLException(GT.tr("The Type whose partition item is not corresponded."),ForestSQLState.INTERNAL_ERROR );
							}
						}
					}
					
					break;
				
				case Types.TIME:
	
					try {
						hashobj.addCol(Time.valueOf(value));
					} catch (Exception e) {
						synchronized(m_formatTime){
			
							long timelong = 0;
							for (int j = 0; j < m_formatTime.length; j++) {
								try {
									timelong = m_formatTime[j].parse(value).getTime();
									hashobj.addCol(new Time(timelong));
									break;
								} catch (ParseException e1) {
								}
			
							}								
			
							if(timelong == 0){								
								//パーティション項目が未対応の型
								throw new PSQLException(GT.tr("The Type whose partition item is not corresponded."),ForestSQLState.INTERNAL_ERROR );
							}
						}
					}
					
					
					break;
				case Types.TIMESTAMP:

					try {
						hashobj.addCol(Timestamp.valueOf(value));
					} catch (Exception e) {
						synchronized(m_formatTimeStamp){
				
							long timelong = 0;
							for (int j = 0; j < m_formatTimeStamp.length; j++) {
								try {
									timelong = m_formatTimeStamp[j].parse(value).getTime();
									hashobj.addCol(new Time(timelong));
									break;
								} catch (ParseException e1) {
								}
				
							}								
				
							if(timelong == 0){								
								//パーティション項目が未対応の型
								throw new PSQLException(GT.tr("The Type whose partition item is not corresponded."),ForestSQLState.INTERNAL_ERROR );
							}
						}
					}
		

					break;
	
				default :
					//パーティション項目が未対応の型
					throw new PSQLException(GT.tr("The Type whose partition item is not corresponded."),ForestSQLState.INTERNAL_ERROR );
			}

		}

		return hashobj.getNodeNo();
		
		}
	}


	/**
	 * すべてのコネクションクローズ
	 * 
	 */
	synchronized public void close() {

		// コネクションリストの接続をすべてクローズ
		for (int i = 0; i < m_servers.size(); i++) {
			ServerInfo srvinf = (ServerInfo) m_servers.get(i);

			Connection con = srvinf.getConnection();

			if ( closeWithoutException(con) )
			{
				try {
					Logger.info("Connection with DB server was closed. (" + ((ForestConnection)con).getURL() + ")");
				}
				catch (Exception e)
				{
					m_logUtil.debug(e.getMessage());
				}
			}
		}

		//キャッシュ更新スレッド停止
		try {
			interrupt();
			join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		m_isClosed = true;//Ver1.1		
	}

	/**
	 * SELECTの分散処理を行うサーバの候補を選択する。
	 * 選択したサーバが `稼働中` でない場合は、次の稼働中のサーバを探す。
	 *
	 * @return int SELECT処理を行うサーバのインデックス（ServerInfo配列へのインデクス）
	 */
	protected int getSvrIdx() {

		boolean retry = false;
		
		for (int i = 0; i < m_servers.size(); i++) {


			//ラウンドロビンが設定されているか、接続先が故障中ならば、サーバIDをインクリメント
			if (m_configInfo.getDistCon() == ConfigInfo.DIST_CON_ROUND_ROBIN || retry == true) {
				m_DistSvrIdx++;
				if (m_servers.size() <= m_DistSvrIdx) {
					m_DistSvrIdx = 0;
				}
			}

			ServerInfo svrInfo = (ServerInfo) m_servers.get(m_DistSvrIdx);
			//接続先候補決定
			if (svrInfo.getStatus() == ServerInfo.SERVER_RUNNING) {
				return m_DistSvrIdx;
			}else{
				retry= true;
			}

		}
		return -1;
	}


	/**
	 * 渡されたServerInfoオブジェクトの配列で、GSC内部のサーバ情報を更新する。
	 *
	 * サーバ情報、テーブル情報をメンバ変数にセットする。
	 *
	 * @param ArrayList servers ServerInfoオブジェクトの配列
	 * @param HashMap tables　テーブル情報リスト
	 */
	protected synchronized  void setGscData(ArrayList servers, HashMap tables) {
		

		Logger.debug("GscData#setGscStatus()");

		//サーバ情報のセット
		if(m_servers == null){
			m_servers = servers;
		}else{
		
			HashMap svrMap = getServerMap(m_servers);
			
			for (int i = 0; i < servers.size(); i++) {
				/*
				 * 更新用のServerInfoオブジェクトの保持しているDBNOと同じ
				 * DBNOを持っている以前のServerInfoオブジェクトを探す。
				 *
				 * 両者のステータスを比較し、「稼動→障害」の変更があれば
				 * コネクションをクローズする。
				 *
				 * 障害が発生していなければ、旧ServerInfoのConnectionオブジェクトを
				 * 新ServerInfoオブジェクトに引き渡す。
				 */
				ServerInfo newSvrInfo = (ServerInfo) servers.get(i);

				Integer dbNo = new Integer(newSvrInfo.getDbNo());
				ServerInfo oldSvrInfo = (ServerInfo) svrMap.get(dbNo);

				if (oldSvrInfo != null) {

				    int oldStatus = oldSvrInfo.getStatus();
				    int newStatus = newSvrInfo.getStatus();

				    if (oldStatus == ServerInfo.SERVER_RUNNING &&
						newStatus == ServerInfo.SERVER_TROUBLE) {
				        Connection con = oldSvrInfo.getConnection();

				        closeWithoutException(con);
				    }else{
						newSvrInfo.setConnection( oldSvrInfo.getConnection() );
				    }
					
					svrMap.remove(dbNo);
				}
			}

			/*
			 * 新しいServerInfo配列でメンバ変数を更新
			 */
			m_servers = servers;

			/*
			 * 新旧比較されなかったServerInfoオブジェクトは、
			 * コネクションをクローズされる。
			 */
			for (Iterator iter = svrMap.values().iterator(); iter.hasNext();) {
				ServerInfo rmSvrInfo = (ServerInfo) iter.next();

				closeWithoutException( rmSvrInfo.getConnection() );
			}
		}

		//テーブル情報をメンバ変数にセット
		m_tables = tables;
		
		//Ver2.1 パーティションテーブルの有無を設定
		m_hasPartition = false;
		m_tableAvailable = true;//Ver3.1オンラインメンテ
		for (Iterator iter = m_tables.values().iterator(); iter.hasNext();) {
			TableInfo tableInfo = (TableInfo) iter.next();
			if( tableInfo.isPartition() ){
				m_hasPartition = true;
				//break; Ver3.1オンラインメンテ
			}
			//Ver3.1オンラインメンテ
			if( tableInfo.getStatus() == TableInfo.TABLE_UNAVAILABLE){
			    m_tableAvailable = false;	    
			}
		}

		if(m_hasPartition)
			Logger.info("DB Partition.");
		else
			Logger.info("DB NoPartition.");
	}

	/**
	 * サーバのステータスを更新するする。
	 * @param servers サーバ情報リスト
	 * @since 2.1
	 */
	protected synchronized  void setGscStatus(ArrayList servers) throws SQLException{
		

		Logger.debug("GscData#setGscStatus()");

		//サーバ情報のセット
		if(m_servers == null){
			m_servers = servers;
		}else{
		

			int cntServers = m_servers.size();

			if ( cntServers != servers.size() ) {
				throw new PSQLException(GT.tr("Number of servers has been changed."),
										ForestSQLState.INTERNAL_ERROR );
			}


			for (int i = 0; i < cntServers; i++) {
				ServerInfo  newSvrInfo = (ServerInfo)servers.get(i);
				ServerInfo  oldSvrInfo = (ServerInfo)m_servers.get(i);

				if ( newSvrInfo.getId() == oldSvrInfo.getId() ){

				    int oldStatus = oldSvrInfo.getStatus();
				    int newStatus = newSvrInfo.getStatus();

				    if(oldStatus == ServerInfo.SERVER_RUNNING &&
				       newStatus == ServerInfo.SERVER_TROUBLE){

				        closeWithoutException( oldSvrInfo.getConnection() );
						oldSvrInfo.setConnection(null);

						//GscInfoリストのステータス更新
						for (Iterator iter = m_gscInfoList.iterator();
							 iter.hasNext();) {
							GscInfo gscInfo = (GscInfo) iter.next();
							if(gscInfo.getId() ==  newSvrInfo.getId()){
								gscInfo.setStatus(ServerInfo.SERVER_TROUBLE);
								break;
							}
							
							Logger.debug("GscData#setGscStatus(): " + gscInfo.toString());
						}
				    }

				    oldSvrInfo.setStatus(newStatus);
				}else{
					//有効なグローバルシステムカタログが無い<-メッセージかえる？
					throw new PSQLException(GT.tr("An effective global system catalog does not exist."),
											ForestSQLState.INTERNAL_ERROR );
				}
			}			
		}
	}

	/**
	 * DBNOからServerInfoオブジェクトを取得するHashMapを生成する
	 *
	 * @param ArrayList serverList ServerInfoオブジェクトの配列
	 *
	 * @return HashMap DBNO(Integer)をキーとし、ServerInfoオブジェクトを値とするHashMap
	 */
	protected HashMap getServerMap(ArrayList serverList) {
		/*
		 * DB NO→サーバ情報のマップ(KEY:DBNO VAKUE:ServerInfo)
		 */
		HashMap serverMap = new HashMap(); 

		for (int i = 0; i < serverList.size(); i++) {
			ServerInfo srvInfo = (ServerInfo) serverList.get(i);
			Integer dbNo = new Integer(srvInfo.getDbNo());

			serverMap.put(dbNo, srvInfo);
		}

		return serverMap;
	}


	/**
	 * @return
	 * @since 1.1
	 */
	public boolean isClosed() {
		return m_isClosed;
	}

	/**
	 * @return
	 * @since 2.1
	 */
	public void setClosed(boolean closed) {
		m_isClosed = closed;
	}

	/**
	 * @return
	 * @since 1.1
	 */
	public int getRetry() {
		return m_configInfo.getRetry();
	}

	/**
	 * @return
	 * @since 2.1
	 */
	public boolean hasPartition() {
		return m_hasPartition;
	}

	/**
	 * @return
	 * @since 3.0
	 */
	public LogUtil getLogUtil() {
		return m_logUtil;
	}

	/**
	 * @return
	 * @since 3.0
	 */
	public boolean canPartitionMode() {
		return m_configInfo.canPartitionMode();
	}

	/**
	 * @return
	 * @since 3.0
	 */
	public boolean getUpdateSyncMode() {
		return m_configInfo.isUpdateSyncMode();
	}

	/**
	 * @param b
	 * @since 3.0
	 */
	public void setUpdateSyncMode(boolean b) {
		m_configInfo.setUpdateSyncMode(b);
	}

	/**
	 * パーティション化2テーブルか？
	 * @param tableName
	 * @return
	 * @throws SQLException
	 * @since 3.0
	 */
	synchronized public boolean isPartition2(String tableName) throws SQLException {

		//スレッドエラーチェック
		chkThredErr();

		TableInfo tblinfo = getTableInfo(tableName);

		return tblinfo.isPartition2();
	}
	
	/**
	 * GSCへのクエリー発行処理
	 * 
	 * @param Sql GSCに発行するSQL
	 * @return
	 * @throws SQLException
	 */
	protected ResultSet executeQueryGSC(String sql) throws SQLException{

		synchronized (m_gscInfoList) {

			for (Iterator iter = m_gscInfoList.iterator(); iter.hasNext();) {
				GscInfo gscInfo = (GscInfo) iter.next();
	
				if (gscInfo.getStatus() != ServerInfo.SERVER_TROUBLE) {
				    String serverURL =  gscInfo.getUrl();
	
					Connection con = null;
	
					try {
	
						con = connect(serverURL,m_prop);
	
						Statement smt = con.createStatement();
	
						return smt.executeQuery(sql);
	
	
					} catch (SQLException e) {
					}finally{
					    closeWithoutException(con);
					}
					
	
				}
			}
		}
		//有効なグローバルシステムカタログが無い
		throw new PSQLException(GT.tr("An effective global system catalog does not exist."), ForestSQLState.INTERNAL_ERROR );

	    
	}

	/**
	 * GSC更新スレッドの内部で例外が発生しているかどうかをチェックする。
	 * 例外が発生していた場合には、その例外を投げる。
	 *
	 * このメソッドは、VMの他のスレッドからGscDataの各メソッドが呼ばれた
	 * 場合に、GSC更新プロセスにおいて例外が発生していたかどうかを
	 * 確認するために使われる。
	 *  
	 * @throws SQLException
	 */
	protected void  chkThredErr() throws SQLException{
			
		if ( m_thredErr != null ) {
			throw m_thredErr;
		}
	}
	
    /**
     * @return tableAvailable を戻します。
     */
    public synchronized boolean isTableAvailable() {
        return m_tableAvailable;
    }

    /**
     * @return tableAvailableがtrueになるまで待機
     */
    public void waitTableAvailable() throws SQLException{

    	//メンテナンス解除待ち
        while ( true )
        {
        	if ( isTableAvailable() )
        		break;

        	try {
        	    //@@待機の仕方は要検討
				Logger.info("Sleeping 3 secs before retrying...");
        		Thread.sleep(3*1000); // sleep 3 secs.
        	} catch (Exception ex) {
				throw new PSQLException(GT.tr("excute internal error.\n{0}",ex), ForestSQLState. INTERNAL_ERROR, ex);

        	}
        }

    }
    
	/**
	 * 初期接続用GSCリストの作成。
	 * JDBC接続文字列で指定されたGSCについて、GscInfoのリストを作成する。
	 *
	 * @param String gscStr GSC指定文字列（カンマ区切りで複数指定可）
	 */
	protected void initialGscList(String gscStr) throws SQLException {

		m_gscInfoList = new ArrayList();
			
		StringTokenizer t = new StringTokenizer( gscStr, "," );

		Logger.debug("GSC CONNECTION STRING: " + gscStr);

		while (t.hasMoreTokens()) {
			Connection con = null;
			String serverURL = t.nextToken();

			Logger.debug("GSC SERVER URL: " + serverURL);

			try {
				con = connect(serverURL, m_prop);
				
				if ( buildGscInfoList(con, serverURL) == true ) {
					//コンフィグレーション情報取得　Ver3.0
					ConfigInfo configInfo = readConfig(con);
					
					//サーバー情報取得
					ArrayList servers = readServerData(con );
					
					m_configInfo = configInfo;
					//テーブル情報取得
					HashMap tables = readTableData(con, servers );
					
					//メンバ変数に格納
					setGscData(servers, tables);
					
					//分散処理が固定時の接続先決定（ランダム） Ver3.0
					if (m_configInfo.getDistCon() == ConfigInfo.DIST_CON_FIX) {
						m_DistSvrIdx = (int) (m_servers.size() * Math.random());
					}
					
					//コネクション管理
					connectServers();
					
					Logger.info("GSC was read. (" + serverURL + ")");
					
					break;
				}
			} catch (SQLException e) {
				Logger.debug(e.getMessage());
			}finally{
				closeWithoutException(con);
			}
		}
	}

	/**
	 * パーティション属性情報取得
	 * @param con
	 * @param tableName
	 * @return
	 * @throws SQLException
	 */
	protected ArrayList readCollumnData(Connection con, String tableName)
		throws SQLException {

		ArrayList clmlist = new ArrayList();

		String sql =
			"select * from FOREST_PARTATR "
				+ "where DBNAME = '" + m_dbName	+ "' "
				+ "and lower(TABLE_NAME) = '" + tableName + "'";
		
		Statement smt = con.createStatement();
		ResultSet rs = smt.executeQuery(sql);
		
		while (rs.next()) {
			PartitionColumnInfo clmInfo = new PartitionColumnInfo();
			clmInfo.setName(rs.getString("COLUMN_NAME").toLowerCase());
			clmInfo.setNumber(rs.getInt("COLUMN_NO"));
			int clmType = ((AbstractJdbc2Connection)con).getSQLType(rs.getString("COLUMN_TYPE").toLowerCase());
			clmInfo.setType(clmType);
		
			clmlist.add(clmInfo);
		
		}
		smt.close();
		return clmlist;
	}

	/**
	 * GSCからコンフィグ情報を読み込む。
	 *
	 * 指定されたConfig名の情報をFOREST_CONFIGから読み込み、
	 * ConfigInfoオブジェクトを生成して返却する。
	 * 指定されたConfig名の設定が見つからない場合にはnullを返却する。
	 *
	 * FIXME: 指定した名前の設定が見つからない場合には例外を投げるべき。
	 *
	 * @param Connection con 初期接続用GSCへのConnectionオブジェクト
	 *
	 * @return ConfigInfo 設定情報を保持したConfigInfoオブジェクト。見つからない場合にはnull。
	 *
	 * @throws SQLException
	 */
	protected ConfigInfo readConfig(Connection con) throws SQLException {

		StringBuffer sql = new StringBuffer("select * from FOREST_CONFIG ");
		sql.append("where CONFIGID = '");
		sql.append(m_configInfo.getConfigID());
		sql.append("'");

		
		Statement smt = null;
		try {
			smt = con.createStatement();
			ResultSet rs = smt.executeQuery(sql.toString());

			rs.next();

			//更新日時
			Date configDate = new Date(rs.getTimestamp("UPDATE_DATE").getTime());

			if(m_configInfo.getConfigDate() == null || 
			   configDate.equals(m_configInfo.getConfigDate()) == false)
			{
				//初回もしくは、更新日時が変更されていたら読み込み

				ConfigInfo configInfo = new ConfigInfo(m_configInfo.getConfigID());


				configInfo.setPartitionMode(rs.getBoolean("PERTITION_MODE"));
				configInfo.setUpdateSyncMode(rs.getBoolean("SYNCRONIZE_MODE"));

				//キャッシュインターバル取得			
				int reflesh = rs.getInt("CACHE_REFLESH") * 1000;
				configInfo.setReflesh(reflesh);

				
				//障害判定タイムアウト取得			
				configInfo.setTimeout(rs.getInt("DEFECT_TIMEOUT"));

				//分散方式
				
				int distCon = rs.getInt("DISTRIBUTED_CONNECTION");
				//分散処理が固定時の接続先決定（ランダム）
				if (distCon == ConfigInfo.DIST_CON_FIX && m_configInfo.getDistCon() != distCon) {
					configInfo.setDistCon(distCon);
				}


				//障害時リトライ回数 Ver1.1
				configInfo.setRetry(rs.getInt("RETRY_COUNT"));

				Logger.info("The configuration file was read. (ID:" + configInfo.getConfigID() + ")");

				configInfo.setConfigDate(configDate);

				return configInfo;
			}
		} catch (SQLException e) {
			
			Logger.info("Reading of a configuration file went wrong. (ID:" + m_configInfo.getConfigID() + ")");
			
			//コンフィグレーションファイル読み込みエラー
			throw new PSQLException(GT.tr("configration data reading error .(ID:{0}) {1}",new Object[]{m_configInfo.getConfigID(), e.getMessage()}),ForestSQLState.INTERNAL_ERROR );
		}finally{
			try {
				if(smt != null)
					smt.close();
			} catch (SQLException e1) {
			}
		
		}
		
		return null;
		
		
	}
	/**
	 * グローバルシステムカタログの読み込み.
	 * プライマリデータベースまたはバックアップデータベースからデータを取得する
	 * 
	 * @throws SQLException
	 */
	protected void readData() throws SQLException{
		
		GscInfo gscInfo = getNextGscInfo();
		
		while(gscInfo != null){		
			Connection con = null;

			try {
				con = gscInfo.getConnection();

				//コンフィグレーション情報取得　Ver3.0
				ConfigInfo configInfo = readConfig(con);

				//サーバー情報取得
				ArrayList servers = readServerData(con );

				synchronized (this) {

					if (configInfo != null) {
	
						m_configInfo = configInfo;
						//テーブル情報取得
						HashMap tables = readTableData(con, servers );
	
 						//メンバ変数に格納
						setGscData(servers, tables);
						
						//GscInfoリストの更新
						buildGscInfoList(con, gscInfo.getUrl());
						
						//分散処理が固定時の接続先決定（ランダム） Ver3.0
						if (m_configInfo.getDistCon() == ConfigInfo.DIST_CON_FIX) {
							m_DistSvrIdx = (int) (m_servers.size() * Math.random());
						}
	
						/*
						 * ユーザDBへ接続
						 */
						connectServers();

					}else{
						/*
						 * サーバのステータス情報のみ更新
						 */
						if (m_servers != null) {
							setGscStatus(servers);
						}
					}
				}

				Logger.info("GSC was read. (" + gscInfo.getUrl() + ")");

				return;
			} catch (SQLException e) {
				Logger.info("Reading of GSC went wrong. (" + gscInfo.getUrl() + ")");
				Logger.info("A server(" + gscInfo.getUrl() + ") is separated.");
				
				//切り離し					
				gscInfo.setStatus(ServerInfo.SERVER_TROUBLE);
				
				//サーバステータス通知Ver3.1
				m_brokenNotifier.notifyGscBroken(gscInfo.getUrl(),e.getMessage(),e.getSQLState());
			} finally {
				closeWithoutException(con);
			}

			//リトライ用GSC取得
			gscInfo = getNextGscInfo();
		}
		//有効なグローバルシステムカタログが無い
		throw new PSQLException(GT.tr("An effective global system catalog does not exist."), ForestSQLState.INTERNAL_ERROR );

	}

	/**
	 * GSCからサーバー情報を読み込む。
	 *
	 * ユーザDBを保持しているサーバの情報（ステータス、URL、データベースNo）を
	 * 格納するServerInfoオブジェクトをサーバ台数分作成し、
	 * サーバID順にArrayListに挿入して返却する。
	 *
	 * @param Connection con 初期接続用GSCへのConnectionオブジェクト
	 *
	 * @return ArrayList ServerInfoの配列
	 *
	 * @throws SQLException
	 */
	protected ArrayList readServerData(Connection con) throws SQLException {

		StringBuffer sql = new StringBuffer(256);
		sql.append("select * ");
		sql.append(" from FOREST_SERVER as a, FOREST_SERVDB as b ");
		sql.append(" where a.SERVERID = b.SERVERID ");
		sql.append(" and b.DBNAME = '");
		sql.append(m_dbName);
		sql.append("'  order by a.SERVERID");

		ArrayList servers = new ArrayList(); //サーバ情報

		Statement smt = con.createStatement();
		ResultSet rs = smt.executeQuery(sql.toString());

		while (rs.next()) {
			ServerInfo srvInfo = new ServerInfo();
			srvInfo.setId(rs.getInt("SERVERID"));
			srvInfo.setStatus(rs.getInt("STATUS"));
			srvInfo.setUrl(rs.getString("URL"));
			srvInfo.setDbNo(rs.getInt("DBNO"));

			servers.add(srvInfo);

		}
		
		smt.close();

		return servers;
	}
	/**
	 * テーブル情報取得
	 * @param con
	 * @param serverMap
	 * @return
	 * @throws SQLException
	 */
	protected HashMap readTableData(Connection con, ArrayList serverList)
		throws SQLException {

		//Ver3.0 ハッシュ関数のローダー 
		HashLoader hloader = HashLoader.getIncetance(false);

		HashMap tables = new HashMap();	//戻り値

		//サーバ情報−テーブル情報の関連用マップ(KEY:DBNO VAKUE:ServerInfo)
		HashMap serverMap = getServerMap(serverList);

		String sql =
			"select * "
				+ "from FOREST_TABLEPART "
				+ "where DBNAME = '" + m_dbName	+ "'";
		
		Statement smt = con.createStatement();
		ResultSet rs = smt.executeQuery(sql);
		
		//テーブル詳細情報取得
		while (rs.next()) {
			TableInfo tblInfo = new TableInfo();
			String tableName = rs.getString("TABLE_NAME").toLowerCase();

			tblInfo.setName(tableName);
		
			sql =
				"select * from FOREST_TABLEPARTDTL"
					+ " where DBNAME = '" + m_dbName	+ "'"
					+ " and lower(TABLE_NAME) = '" + tableName + "'"
//					+ " order by PART_NO";  //Ver2.0
					+ " order by PRIORITY"; //Ver2.0
		
			Statement smt2 = con.createStatement();
			ResultSet rs2 = smt2.executeQuery(sql);
		
//			ArrayList svrList = new ArrayList();//Ver2.0
			while (rs2.next()) {


				//DBNOからサーバ情報を特定して、リストにセット
				Integer dbNo = new Integer(rs2.getInt("DBNO"));
		
				ServerInfo svrInfo =
					(ServerInfo) serverMap.get(dbNo);
				//Ver2.0 --> 
				//パーティションNoをリストの添字にする。
				////多重化テーブルは、NULLがセットされているので、indexは0
				//int index = rs2.getInt("PART_NO");
				//@@暫定（パーティションナンバーが０から振られていない場合）
				//if(svrList.size() <= index){
				//	index = svrList.size();
				//}
				//svrList.add(index, svrInfo);

				int partNo = rs2.getInt("PART_NO");
				tblInfo.setServers(partNo, svrInfo);
				//Ver2.0 <-- 
		
			}
			smt2.close();
			
//			tblInfo.setServers(svrList);Ver2.0
			
//Ver2.0
//  		int partCount = rs.getInt("PART_COUNT");
//			if (1 < partCount) {
			tblInfo.setType(rs.getInt("PART_TYPE"));
			tblInfo.setPartitionNum(rs.getInt("PART_COUNT"));
			tblInfo.setStatus( rs.getInt("STATUS") );			
			if (tblInfo.isPartition()) {
				//パーティション属性情報取得
				ArrayList clmlist = readCollumnData(con, tableName);
		
				tblInfo.setColumnList(clmlist);

//				tblInfo.setPartition(true);//Ver2.0
			} 
//Ver2.0    else {
//				tblInfo.setPartition(false);
//			}


			//Ver3.0 
			//ハッシュクラス名を取得
			String className = rs.getString("HASH_NAME");
			if( className == null) {
				//TODO デフォルト？
				tblInfo.setHash(new Hash01());

			}else{
			
				//ハッシュクラスをロードして格納
				try {
					Hash_I hashobj = (Hash_I) hloader.loadClass(className,con).newInstance();
	
					//ハッシュクラスをテーブル情報に格納
					tblInfo.setHash(hashobj);
	
				} catch (Exception e) {
					//ハッシュ関数未定義
					//パーティション項目用の関数が不正です。
					throw new PSQLException(GT.tr("Partition No decision function is illegal. {0}:{1}",new Object[]{className,e.getMessage()}),ForestSQLState.INTERNAL_ERROR );
	
				}
			}

			
			tables.put(tableName, tblInfo);
		
		}

		smt.close();
		
		return tables;
	}
	/** キャッシュ更新スレッド
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		Logger.info("The updating thread of GSC cash is started.");

		while (true) {

			//インターバル
			try {

				if(interrupted())
					break;
				int reflesh = m_configInfo.getReflesh();
				if( reflesh == 0 ){
					break;
				}
				sleep(reflesh);

			} catch (InterruptedException e) {
				// 終了処理へ
				break;
			}


			//グローバルシステムカタログの読み込み
			try {
				readData();
			} catch (SQLException e1) {
				m_thredErr = e1;
				break;
			}


		}

		Logger.info("The updating thread of GSC cash is ended.");
	}
	/**
	 * グローバルシステムカタログの更新
	 * <pre>
	 * サーバの状態フラグが故障中に設定されている場合、
	 * 該当するフラグを変更する
	 * </pre>
	 * @param id
	 */
	protected void updateDBstatus(ServerInfo serverInfo, String query, Exception ex) {

		int id = serverInfo.getId();
		String url = serverInfo.getUrl();
		String msg = ex.getMessage();
		String status;

		if(ex instanceof SQLException){
			status =((SQLException)ex).getSQLState();
		}else{
			status ="XX000";
		}
		
		//ステータス
		StringBuffer sqlStatus = new StringBuffer(128);
		sqlStatus.append("update  FOREST_SERVER ");
		sqlStatus.append(" set STATUS = ");
		sqlStatus.append(ServerInfo.SERVER_TROUBLE);
		sqlStatus.append(" where SERVERID = ");
		sqlStatus.append(id);
		
		
		//BrokenLog
		StringBuffer sqlLog = new StringBuffer(256);
		sqlLog.append("INSERT INTO forest_brokenlog (serverid, datetime, msg, status, client, query) ");
		sqlLog.append("VALUES( ");
		sqlLog.append(id);
		sqlLog.append(", '");
		sqlLog.append(new java.util.Date());
		sqlLog.append("', '");
		sqlLog.append(msg.replaceAll("'", "''"));
		sqlLog.append("', '");
		sqlLog.append(status);
		sqlLog.append("', '");
		sqlLog.append(m_client);
		sqlLog.append("', '");
		sqlLog.append(query.replaceAll("'","''"));
		sqlLog.append("')");
		

		//Ver3.1 ステータス通知
		m_brokenNotifier.notifyServerBroken(url,msg,status,query);

		synchronized (m_gscInfoList) {

			boolean updateStatus = false;
			for (Iterator iter = m_gscInfoList.iterator(); iter.hasNext();) {
				GscInfo gscInfo = (GscInfo) iter.next();
			    String serverURL =  gscInfo.getUrl();

				Logger.debug("updateDBstatus: " + gscInfo.toString());
	
				if(gscInfo.getId() == id){
				
					//サーバステータス通知Ver3.1
					m_brokenNotifier.notifyGscBroken(serverURL,msg,status);

					gscInfo.setStatus(ServerInfo.SERVER_TROUBLE);
	
				}else if (gscInfo.getStatus() != ServerInfo.SERVER_TROUBLE) {

					Connection con = null;

					try {
						con = connect(serverURL,m_prop);
	
						Statement smt = con.createStatement();
	
						smt.executeUpdate(sqlStatus.toString());
						smt.executeUpdate(sqlLog.toString());
	
						smt.close();
						updateStatus = true;
	
					} catch (SQLException e) {
						//サーバステータス通知Ver3.1

						m_brokenNotifier.notifyGscBroken(serverURL,e.getMessage(),e.getSQLState());
	
						gscInfo.setStatus(ServerInfo.SERVER_TROUBLE);
	
	
					} finally {
						closeWithoutException(con);
					}
	
				}
			}
			if(!updateStatus){
				
				//有効なグローバルシステムカタログが無い
				m_thredErr = new PSQLException(GT.tr("An effective global system catalog does not exist."), ForestSQLState.INTERNAL_ERROR ,ex);

			}
		}
	}
	/**
	 * 指定した（GSCへの）ConnectionからGSCのリストを読み出し、
	 * GscData内のGscInfo配列を更新する。
	 * その際、指定したURLのGSCをGscInfo配列の先頭に配置する。
	 *
	 * SQL例外、あるいはGSC情報がひとつも見つからなかった場合は失敗とする。
	 * 
	 * @param Connection con （GSCへの）Connectionオブジェクト
	 * @param String serverURL 先頭に配置するGSCのURL表記
	 *
	 * @return GscInfo配列を更新した場合true、失敗により更新しなかった場合false
	 */
	protected boolean buildGscInfoList(Connection con, String serverURL) {
		String sql = "select URL, DBNAME, s.SERVERID, STATUS from FOREST_GSC g, FOREST_SERVER s where g.SERVERID = s.SERVERID";

		ArrayList servers = new ArrayList();

		try {
			Statement smt = con.createStatement();
			ResultSet rs = smt.executeQuery(sql);
			
			while (rs.next()) {
				String gscUrl = rs.getString("URL")
					+ rs.getString("DBNAME");

				Logger.debug(" GSC URL    : " + rs.getString("URL"));
				Logger.debug(" GSC DB     : " + rs.getString("DBNAME"));
				Logger.debug(" GSC STATUS : " + rs.getString("STATUS"));

				GscInfo gscInfoNew = new GscInfo(rs.getInt("SERVERID"),
												 rs.getInt("STATUS"),
												 gscUrl);

				/*
				 * GSC情報の読み込みに使用したサーバを
				 * GscInfo配列の先頭に配置する
				 */
				if (serverURL.equals(gscUrl)) {
					servers.add(0, gscInfoNew);
				} else {
					servers.add(gscInfoNew);
				}
			}

			rs.close();
			smt.close();
		} catch (Exception e) {
			return false;
		}

		/*
		 * 読み込んだGSCが1つ以上あれば、リストを更新
		 */
		if (servers.size() > 0) {
			m_gscInfoList = servers;
			return true;
		}
		
		return false;
	}

	/**
	 * 有効なGscInfoオブジェクトを取得する。
	 * その際、GscInfoオブジェクトは、GSCへの接続を行ってコネクションも確立して返却される。
	 *
	 * @return GscInfo 利用可能なGscInfoオブジェクト。存在しない場合にはnull。
	 */
	protected GscInfo getNextGscInfo() {
		Connection con = null;

		synchronized (m_gscInfoList) {

			for (Iterator iter = m_gscInfoList.iterator(); iter.hasNext();) {
				GscInfo gscInfo = (GscInfo) iter.next();
					
				if (gscInfo.getStatus() != ServerInfo.SERVER_TROUBLE) {
					String serverURL = gscInfo.getUrl();
	
					try {
						con = connect(serverURL, m_prop);
	
						gscInfo.setConnection(con);

						return gscInfo;
					} catch (SQLException e) {
						Logger.info("Reading of GSC went wrong. (" + gscInfo.getUrl() + ")");
						Logger.info("A server(" + gscInfo.getUrl() + ") is separated.");
						
						/*
						 * GSCに接続できなかった場合、
						 * 切り離し対象エラーであれば縮退する
						 */
						if( DistError.isBroken(e) == true ) {
							//サーバーステータスも含めて切り離し
							
							//サーバIDからサーバ情報を取得する
							int server_id = gscInfo.getId();
							for (Iterator iterator = m_servers.iterator() ;
								 iterator.hasNext() ;)
								{
									ServerInfo srvinf = (ServerInfo) iterator.next();
									if(srvinf.getId() == server_id){
										
										//ステータスに故障中をセット
										srvinf.setStatus(ServerInfo.SERVER_TROUBLE);
										
										//グローバルシステムカタログ更新
										updateDBstatus(srvinf, "CONNECT", e );
										
										break;
									}
								}
						}

						//グローバルシステムカタログ更新が呼ばれていれば、GscInfoのステータスもSERVER_TROUBLEになっているので、
						//切り離し対象エラーではないか、上記グローバルシステムカタログ更新が呼ばれていない場合、
						//GscInfoの切り離しを行う
						if(	gscInfo.getStatus() == ServerInfo.SERVER_RUNNING) {
							//切り離し					
							gscInfo.setStatus(ServerInfo.SERVER_TROUBLE);
							
							//サーバステータス通知Ver3.1
							m_brokenNotifier.notifyGscBroken(gscInfo.getUrl(),e.getMessage(),e.getSQLState());
							
						}
						
						closeWithoutException(con);
					}
				}
			}

		}
		return null;
	}
	
	/**
	 * 発生した例外を無視してConnectionをクローズする
	 *
	 * @param Connection con クローズするConnectionオブジェクト
	 *
	 * @return boolean 正常にクローズできればtrue、それ以外ならfalse
	 */
	private boolean closeWithoutException(Connection con)
	{
		boolean rc = false;

		if ( con==null )
			return rc;

		try {
			con.close();
			rc = true;
		}
		catch (Exception e)
		{
			/* no need to handle exception */
		}

		return rc;
	}
}
