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

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.common.SystemManager;
import org.opengion.fukurou.util.LogWriter;
import org.opengion.fukurou.util.Cleanable;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.ApplicationInfo;
import org.opengion.fukurou.db.DBUtil;

/**
 * systemId に対応したユーザー情報を作成するファクトリクラスです。
 *
 * UserInfoオブジェクトは，キャッシュせずに、要求都度、データベースを検索します。
 * これは、ユーザー登録が、他システムより行われる可能性を考慮している為です。
 * ユーザーオブジェクトの要求は、基本的にログイン時のみで、その後セッションに
 * キープされます。
 *
 * 検索するカラムには、必ず、USERID,LANG,NAME,ROLES,DROLES がこの順番で含まれており、
 * 絞込み条件(?パラメータ)として、SYSTEM_ID,USERID がこの順番で指定される必要があります。
 * (カラム名は関係ありません。並び順と意味が重要です。)
 * また、検索順(ORDER BY)は、優先順位の低い順に検索してください。使用するのは、一番最後に
 * 検索された行を使用します。
 * ユーザーリソースは、RESOURCE_USER_DBID で指定のデータベースから取得します。
 * 未定義の場合は、RESOURCE_DBID から、それも未定義の場合は デフォルトの接続先を
 * 使用します。
 *
 * SYSTEM_ID='**' は、共通リソースです(ROLESも共通に設定する必要があります。)。
 * これは、システム間で共通に使用されるリソース情報を登録しておきます。
 * SYSTEM_ID は、指定のシステムIDと**を検索対象にします。**は、全システム共通の
 * 指定のシステムIDと**と両方存在する場合は、指定のシステムIDが優先されます。
 *
 * ver4 では、デフォルトロールという考え方がなくなりましたので、画面のロールに、
 * (*)を明示的に追加し、RWMODE を指定する必要があります。
 *
 * @og.rev 4.0.0 (2004/12/31) 新規作成
 * @og.group リソース管理
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class UserInfoFactory {

	private static final String SYSTEM_ID = HybsSystem.sys( "SYSTEM_ID" );

	// ユーザーリソースの接続先を、取得します。
	private static String dbid = StringUtil.nval(
								HybsSystem.sys( "RESOURCE_USER_DBID" ) ,
								HybsSystem.sys( "RESOURCE_DBID" )
							) ;

	// ユーザーリソースのキー指定読み込みのクエリー
	private static String query = HybsSystem.sys( "RESOURCE_USER_SQL" );
	private static String queryRole = HybsSystem.sys( "RESOURCE_USER_ROLE_SQL" );

	/** コネクションにアプリケーション情報を追記するかどうか指定 */
	public final static boolean USE_DB_APPLICATION_INFO  = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ;

	// 4.0.0 (2005/01/31) Cleanable インターフェースによる初期化処理
	static {
		Cleanable clr = new Cleanable() {
			public void clear() {
				UserInfoFactory.clear();
			}
		};

		SystemManager.addCleanable( clr );
	}

	private final static Object lock = new Object();

	// GE20 ユーザーロール情報永続化テーブル 読み込み用SQL
	// 4.3.4.0 (2008/12/01) ROLE='*'も検索できるようにする
//	private final static String QUERY_GE20 = "select PARAM_ID,PARAM from GE20 where SYSTEM_ID = ? and USERID = ? and ROLES = ? ";
	private final static String QUERY_GE20 = "select PARAM_ID,PARAM from GE20 where SYSTEM_ID = ? and USERID = ? and ROLES in ( ?, '*') and FGJ = '1' ORDER BY ROLES";

	/**
	 *  デフォルトコンストラクターをprivateにして、
	 *  オブジェクトの生成をさせないようにする。
	 *
	 */
	private UserInfoFactory() {
	}

	/**
	 * UserInfo オブジェクトを取得します。
	 *
	 * UserInfoオブジェクトは，キャッシュせずに、要求都度、データベースを検索します。
	 * これは、ユーザー登録が、他システムより行われる可能性を考慮している為です。
	 * ユーザーオブジェクトの要求は、基本的にログイン時のみで、その後セッションに
	 * キープされます。
	 *
	 * @og.rev 3.7.0.4 (2005/03/18) ゲストログイン機能追加
	 * @og.rev 4.0.0.0 (2007/10/31) ロール指定でのログイン機能追加
	 * @og.rev 4.3.4.0 (2008/12/01) GE20(ユーザー定数)へ登録するかのフラグへの対応
	 * @og.rev 4.4.0.0 (2009/08/02) データロール対応
	 *
	 * @param   userID    ユーザーID
	 * @param   ipAddress ログイン端末のIPアドレス
	 * @return  UserInfo  オブジェクト
	 */
	public static UserInfo newInstance( final String userID,final String ipAddress,final String roles ) {
		// 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfo オブジェクトを設定
		ApplicationInfo appInfo = null ;
		if( USE_DB_APPLICATION_INFO ) {
			appInfo = new ApplicationInfo();
			// ユーザーID,IPアドレス,ホスト名
			appInfo.setClientInfo( userID,ipAddress,null );
			// 画面ID,操作,プログラムID
			appInfo.setModuleInfo( "UserInfoFactory",null,"newInstance" );
		}

		String[] args;
		String[][] vals;

		if( roles == null || roles.length() == 0 ) {
			args = new String[] { SYSTEM_ID,userID };
			synchronized( lock ) {
				vals = DBUtil.dbExecute( query,args,appInfo,dbid );
			}
		}
		// 4.0.0.0 (2007/10/31)
		else {
			args = new String[] { SYSTEM_ID,userID,roles };
			synchronized( lock ) {
				vals = DBUtil.dbExecute( queryRole,args,appInfo,dbid );
			}
		}

		final UserInfo info ;

		int len = vals.length ;	// システムID ** を含む。
		if( len >= 1 && vals[0].length >= 5 ) {
			// システムIDでソートされる。SYSTEM_ID="**"は最初に現れるので、最後を取得
			info = new UserInfo(
								userID		,			// userID
								vals[len-1][1]	,		// lang
								vals[len-1][2]	,		// jname
								vals[len-1][3]	,		// roles
								vals[len-1][4]	,		// droles // 4.4.0.0 (2009/08/02)
								SYSTEM_ID		,		// systemId
								ipAddress		,		// ipAddress
								appInfo			) ;		// ApplicationInfo
		}
		else {
			String errMsg = "UserInfo のデータ(USERID,LANG,NAME,ROLES,DROLES)が取得できません。"
						+ " Key [" + userID + "]"
						+ " SQL [" + query + "]" ;
			LogWriter.log( errMsg );
			throw new HybsSystemException( errMsg );
		}

		// GE20 ユーザーロール情報永続化テーブル読み込み
		String[] argsGe20 = new String[] { SYSTEM_ID,userID,vals[len-1][3] };
		String[][] valsGe20 ;
		synchronized( lock ) {
			valsGe20 = DBUtil.dbExecute( QUERY_GE20,argsGe20,appInfo,dbid );
		}
		for( int i=0; i<valsGe20.length; i++ ) {
//			info.setAttribute( valsGe20[i][0], valsGe20[i][1] );
			// 4.3.4.0 (2008/12/01) GE20から読み込んでいるので、当然保存しない
			info.setAttribute( valsGe20[i][0], valsGe20[i][1], false );
		}

		return info ;
	}

	/**
	 * UserInfoFactoryをクリアします。
	 *
	 */
	public static void clear() {
		synchronized( lock ) {
			dbid = StringUtil.nval(
									HybsSystem.sys( "RESOURCE_USER_DBID" ) ,
									HybsSystem.sys( "RESOURCE_DBID" )
								) ;
			query = HybsSystem.sys( "RESOURCE_USER_SQL" );
			queryRole = HybsSystem.sys( "RESOURCE_USER_ROLE_SQL" );
		}
	}
}
