/*
 * 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.db;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.SchemaFactory;

import javax.xml.XMLConstants;

import org.xml.sax.SAXException;

import org.opengion.fukurou.xml.jaxb.dbid.DbConfig;
import org.opengion.fukurou.xml.jaxb.dbid.DbDriver;
import org.opengion.fukurou.xml.jaxb.dbid.Dbid;
import org.opengion.fukurou.util.LogWriter;

/**
 * DB設定XMLの内容をJAXBを利用してロードする
 * Driverをロードする
 * 上記２つの機能を備えたクラスです
 *
 * 外部からはgetDbidメソッドを利用してDB設定（ExpandedDbid型）を取得します。
 * DB設定情報が無い場合にXMLを読みにいきます。
 * このDBIDを決めるキーは、内部取り込み字に、大文字変換されますので、大文字・
 * 小文字の区別はありません。
 *
 * @og.rev 4.0.0 (2007/10/25) 新規作成
 * @og.group 初期化
 *
 * @version  4.0
 * @author 高橋正和
 * @since   JDK6.0,
 */
public class DatabaseConfig {
	// fukurou内で完結させるため、HybsDataからは読み込まずにここに書く
	private static final String DEFAULT_DRIVER	 = "oracle.jdbc.OracleDriver";
	// XMLファイル関連
//	private transient final String XmlFilename;
	private final String XmlFilename;
	private final static String SCHEMA_FILENAME = "DBConfig.xsd";//xsdはfukurou.xml.jaxb.dbidパッケージに置く

//	private transient final Map<String, EDbid> dbidMap = new HashMap<String, EDbid>();
//	private transient List<String> driverList = new ArrayList<String>();
	private final Map<String, EDbid> dbidMap = new HashMap<String, EDbid>();
	private List<String> driverList = new ArrayList<String>();

	private static final String CR = System.getProperty( "line.separator" );

	/**
	 * 初期値を使ってXMLを読み込む
	 * XmlFilenameの初期値は../DBConfig.xml
	 *
	 * @og.rev 4.3.1.1 (2008/08/23) 自分のコンストラクターを呼ぶように修正
	 */
	public DatabaseConfig() {
//		XmlFilename = "../DBConfig.xml";
		this( "../DBConfig.xml" );
	}

	/**
	 * XMLファイルの名前を指定して読み込む
	 *
	 * @param xmlfile String
	 */
	public DatabaseConfig( final String xmlfile ) {
		XmlFilename = xmlfile;
	}

	/**
	 * dbidKeyをキーにしてExpandedDbid型でマップの内容を返す。
	 * 存在しない場合はNULLを返します。
	 * キーが無い場合に初期化を行う。
	 *
	 * @og.rev 4.0.0.1 (2007/12/04) EDbid#clone() 廃止
	 *
	 * @param key String XMLで登録したdbidKey
	 * @return EDbid型オブジェクト
	 */
	public EDbid getDbid( final String key ) {
		synchronized ( dbidMap ) {
			if( dbidMap.isEmpty() ) {
				init();
			}

			return dbidMap.get( key.toUpperCase( Locale.JAPAN ) ) ;
		}
	}

	/**
	 * マップをクリアします。
	 * XMLファイルを再読み込みする場合に使用します。
	 */
	public void reload() {
		synchronized ( dbidMap ) {
			dbidMap.clear();
		}
	}

	/**
	 * 初期化処理
	 * DB設定XMLファイルを読み込みます
	 * getDbidメソッドが呼ばれた時にdbidMap==nullの条件で呼ばれます。
	 */
	private void init() {
		loadXML( XmlFilename, SCHEMA_FILENAME );
		loadDriver();
	}

	/**
	 * 登録されているドライバをロードします
	 *
	 * @og.rev 4.0.0.0 (2007/11/22) エラー処理ハンドリングを強化
	 * @og.rev 4.0.0.1 (2007/12/04) EDbid#clone() 廃止
	 * @og.rev 4.1.0.1 (2008/01/21) 登録時に、大文字に変換する。
	 * @og.rev 4.2.0.0 (2008/02/26) URL,USER,PASSが空文字の場合に環境変数を設定するよう変更
	 *
	 * @param filename  String  XMLで登録した DBID接続先情報(DBConfig.xml)
	 * @param schemaname String XMLで規定した スキーマ情報(DBConfig.xsd)
	 */
	private void loadXML( final String filename, final String schemaname ) {
		DbConfig dbconfig = null;
		try {
			final JAXBContext jaxbc			 = JAXBContext.newInstance( "org.opengion.fukurou.xml.jaxb.dbid" );
			final Unmarshaller unmarshaller	 = jaxbc.createUnmarshaller();

			//fukurou.xml.jaxb.dbidパッケージに置いたXSDファイルを読み込んでチェックに使用します。
			//ObjectFactoryを読み込むやや強引な方法。もっといい方法があるのかもしれない。
			final Class<?> cls		 = Class.forName( "org.opengion.fukurou.xml.jaxb.dbid.ObjectFactory" );	// 4.3.3.6 (2008/11/15) Generics警告対応
			final URL schemaURL		 = cls.getResource( schemaname );
			final ClassLoader clsl	 = cls.getClassLoader();
			final URL xmlURL		 = clsl.getResource( filename );
			if( xmlURL != null ) {
				unmarshaller.setSchema( SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI ).newSchema(schemaURL) );
				dbconfig = (DbConfig) unmarshaller.unmarshal( xmlURL );
			}
		}
		catch( JAXBException je ) {
			final String errMsg  = je
				+ ":"
				+ filename
				+ "もしくは"
				+ schemaname
				+ "が存在していないか、XMLがスキーマに沿っていません。"
				+ CR;
			// Caught した Exception を throw に含めます。
			throw new RuntimeException(errMsg,je);

		} catch ( SAXException ex ) {
			LogWriter.log( ex );
			throw new RuntimeException( ex );
		} catch ( ClassNotFoundException ex ) {
			LogWriter.log( ex );
			throw new RuntimeException( ex );
		}

		if( dbconfig == null ) {
			final String errMsg = "dbconfigが取得できませんでした。"
				+ "DBConfig.xml = "
				+ filename + CR;
			throw new RuntimeException( errMsg );
		}

		//ドライバリスト取得
		//final DbDriver dbd;
		final DbDriver	dbd = dbconfig.getDbDriver();
	//	synchronized ( driverList ) {
			if( dbd == null ) {
				//nullの場合は標準ドライバ
				driverList.add( DEFAULT_DRIVER );
			}
			else {
				driverList	= dbd.getClazz();
			}
	//	}

		//DB Config要素取得
		final List<Dbid> idList = dbconfig.getDbid();		// 4.3.3.6 (2008/11/15) Generics警告対応

		//DBIDタグ要素をマップに入れます
		for( int i=0;i<idList.size();i++ ) {
			final Dbid dbid = idList.get(i);				// 4.3.3.6 (2008/11/15) Generics警告対応
	 		// 4.1.0.1 (2008/01/21) 登録時に、大文字に変換する。
			String dbidKey = dbid.getDbidKey().toUpperCase( Locale.JAPAN );

			EDbid edbid = new EDbid();
			//拡張DBIDに値をセット
//			edbid.setDbidKey(	dbid.getDbidKey()	);
			edbid.setDbidKey(	dbidKey				);	// 4.1.0.1 (2008/01/21)
			if( "".equals( dbid.getUrl() ) ){ // 4.2.0.0 (2008/02/19) 空文字の場合の処理追加
				edbid.setUrl( System.getenv( "REALM_URL" ) ); 
			}
			else{
				edbid.setUrl( dbid.getUrl() );
			}
			if( "".equals( dbid.getUser() ) ){
				edbid.setUser( System.getenv( "REALM_NAME" ) );
			}
			else{
				edbid.setUser( dbid.getUser() );
			}
			if( "".equals( dbid.getPassword() ) ){ // パスワード無しは想定外
				edbid.setPassword( System.getenv( "REALM_PASSWORD" ) );
			}
			else{
				edbid.setPassword( dbid.getPassword() );
			}
			edbid.setMaxcount(	dbid.getMaxcount()	);
			edbid.setMincount(	dbid.getMincount()	);
			edbid.setPooltime(	dbid.getPooltime()	);
			edbid.setReadonly(	dbid.isReadonly()	);
			edbid.setApplicationInfo( dbid.isApplicationInfo() );

//			dbidMap.put( dbid.getDbidKey().toUpperCase( Locale.JAPAN ),edbid );
			dbidMap.put( dbidKey,edbid );	// 4.1.0.1 (2008/01/21)
		}
	}

	/**
	 * 登録されているドライバをロードします
	 */
	private void loadDriver() {
	//	synchronized( driverList ) {
			for( int i=0; i < driverList.size(); i++) {
				try {
					Class.forName( driverList.get(i) );
				} catch ( ClassNotFoundException ex ) {
					final String errMsg = "ドライバクラスが見つかりません。["
								+ driverList.get(i) + "]" ;
					LogWriter.log( errMsg );
					LogWriter.log( ex );
				}
			}
	//	}
	}
}
