/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.fukurou.process;

import org.opengion.fukurou.util.Closer ;

import java.util.Map ;
import java.util.HashMap ;
import java.util.List ;
import java.util.ArrayList ;
import java.sql.Connection;

/**
 * ConnDataFactory は、ConnData オブジェクトを構築する為のファクトリクラスです。
 *
 * Connection 情報は、この ファクトリクラス でキャッシュされ、使いまわされます。
 * ここでは、必要なだけ、コネクションを作成しますが、最大管理数と、使用期間に
 * 関しては、内部的に管理している設定値で決定します。
 * バッチ的に使用する場合は、ほとんど効果を発揮しません。あくまで、APサーバー等
 * での使用を元に、永続的に繰返し使われるケースを想定しています。
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class ConnDataFactory {
	private static final long MAX_ALIVE_TIME = 1800000 ; // 30 分
	private static final int  MAX_LIST_COUNT = 3 ; // キャッシュの最大数

	private static final Map<Integer,List<ConnData>> connMap = new HashMap<Integer,List<ConnData>>();
	private static final String ORACLE_DRIVER = "oracle.jdbc.OracleDriver";

	/** リターンコード  System.getProperty("line.separator")  */
	public static final String CR = System.getProperty("line.separator");

	static {
		try {
			Class.forName( ORACLE_DRIVER );
		}
		catch( ClassNotFoundException ex ) {
			String errMsg = "ドライバクラスが見つかりません。[" + ORACLE_DRIVER + "]";
			throw new RuntimeException( errMsg,ex );
		}
	}

	/**
	 * コンストラクタを private 化する事で、オブジェクト生成を出来なくします。
	 */
	private ConnDataFactory() {}

	/**
	 * ConnData オブジェクトを取得します。
	 * キャッシュに存在する場合は、キャッシュから、そうでない場合は、新規作成します。
	 *
	 * @param	url		String	接続先URL
	 * @param	user	String	接続ユーザー
	 * @param	passwd	String	パスワード
	 * @return	ConnData	正常:true/異常:false
	 */
	public static ConnData createConnData( final String url,final String user, final String passwd ) {
		int uniq = ( url + "," + user + "," + passwd ).hashCode() ;
		ConnData connData = null;
		synchronized( connMap ) {
			List<ConnData> list = connMap.get( uniq );
			if( list != null && ! list.isEmpty() ) {
				int adrs = list.size() - 1;
				connData = list.remove( adrs );	// 最後の要素を取り出す。
			}
		}

		if( connData == null ) {
			connData = new ConnData( url,user,passwd,uniq );
		}
		return connData;
	}

	/**
	 * ConnData オブジェクトの終了(close)処理を行います。
	 * 正常終了時(isOK=true)には、commit を実行し、キャッシュに戻します。
	 * ただし、MAX_ALIVE_TIME で指定した時間以上経過した場合は、
	 * オブジェクトは破棄します。
	 * 異常終了時の場合(isOK=false)、rollback を実行し、オブジェクトは破棄します。
	 *
	 * @param	isOK boolean	正常:true/異常:false
	 */
	public static void closeConnData( final ConnData connData,final boolean isOK ) {
		if( connData != null ) {
			Connection conn = connData.getConnection();
			if( isOK ) {
				Closer.commit( conn );
				if( connData.getCreateTime() + MAX_ALIVE_TIME > System.currentTimeMillis() ) {
					int uniq = connData.getUniq();
					synchronized( connMap ) {
						List<ConnData> list = connMap.get( uniq );
						if( list == null ) { list = new ArrayList<ConnData>(); }
						if( list.size() >= MAX_LIST_COUNT ) {
							Closer.connClose( conn );
						}
						else {
							list.add( connData );
							connMap.put( uniq,list );
						}
					}
				}
				else {
					Closer.connClose( conn );
				}
			}
			else {
				Closer.rollback( conn );
				Closer.connClose( conn );
			}
		}
	}

	/**
	 * 内部 ConnData キャッシュを初期化します。
	 *
	 */
	public static void clear() {
		synchronized( connMap ) {
			for( List<ConnData> list : connMap.values() ) {
				for( ConnData connData : list ) {
					Connection conn = connData.getConnection();
					Closer.connClose( conn );
				}
				list.clear();
			}
			connMap.clear();
		}
	}
}
