package org.phosphoresce.commons.ldaps;

import java.io.Serializable;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;

/**
 * LDAPマネージャ環境設定クラス<br>
 *
 * @author Kitagawa<br>
 *
 *<!--
 * 更新日		更新者			更新内容
 * 2008/11/27	Kitagawa		新規作成
 *-->
 */
public class LDAPManagerConfig implements Serializable {

	// Static Field

	/** 検索スコープ (名前付きオブジェクト検索) */
	public static final int SEARCH_SCOPE_OBJECT = SearchControls.OBJECT_SCOPE;

	/** 検索スコープ (名前付きオブジェクト1レベル検索) */
	public static final int SEARCH_SCOPE_ONELEVEL = SearchControls.ONELEVEL_SCOPE;

	/** 検索スコープ (名前付きオブジェクトサブツリー検索) */
	public static final int SEARCH_SCOPE_SUBTREE = SearchControls.SUBTREE_SCOPE;

	/** SSL接続セキュリティレベル(none) */
	public static final String SECURITY_AUTHENTICATION_NONE = "none";

	/** SSL接続セキュリティレベル(simple) */
	public static final String SECURITY_AUTHENTICATION_SIMPLE = "simple";

	/** SSL接続セキュリティレベル(strong) */
	public static final String SECURITY_AUTHENTICATION_STRONG = "strong";

	/** ディフォルトLDAPポート */
	public static final int DEFAULT_LDAP_PORT = 389;

	/** ディフォルトLDAPポート(SSL経由) */
	public static final int DEFAULT_LDAP_SSL_PORT = 636;

	/** ディフォルトLDAPバージョン */
	public static final int DEFAULT_LDAP_VERSION = 3;

	/** ディフォルト匿名接続フラグ */
	public static final boolean DEFAULT_ANONYMOUS_BIND = false;

	/** ディフォルトSSL接続フラグ */
	public static final boolean DEFAULT_SSL_BIND = false;

	/** ディフォルトSSL接続セキュリティレベル */
	public static final String DEFAULT_SSL_SECURITY_LEVEL = SECURITY_AUTHENTICATION_NONE;

	/** ディフォルト検索スコープ */
	public static final int DEFAULT_SEARCH_SCOPE = SEARCH_SCOPE_ONELEVEL;

	/** ディフォルト検索数限界サイズ(無限) */
	public static final int DEFAULT_COUNT_LIMIT = 0;

	/** ディフォルト検索数タイムアウト(無限) */
	public static final int DEFAULT_TIME_LIMIT = 0;

	/** ディフォルト検索中のリンク間接参照フラグ(逆参照しない) */
	public static final boolean DEFAULT_DEREF_LINK = false;

	/** ディフォルト検索結果としてオブジェクトを返すフラグ(名前とクラスのみ返却) */
	public static final boolean DEFAULT_RETURN_OBJECT = false;

	// Class Field

	/** LDAPホスト */
	private String host;

	/** LDAPポート */
	private int port;

	/** LDAPバージョン */
	private int version;

	/** ベースDN */
	private String baseDn;

	/** 匿名接続フラグ */
	private boolean anonymousBind;

	/** SSL接続フラグ */
	private boolean sslConnection;

	/** SSL接続セキュリティレベル */
	private String sslSecurityLevel;

	/** 接続ユーザーDN */
	private String userDn;

	/** 接続パスワード */
	private String userPassword;

	/** 検索スコープ */
	private int searchScope;

	/** 検索数限界サイズ */
	private int countLimit;

	/** 検索数タイムアウト */
	private int timeLimit;

	/** 検索中のリンク間接参照フラグ */
	private boolean derefLink;

	/** 検索結果としてオブジェクトを返すフラグ(falseの場合は名前とクラスのみ返却) */
	private boolean returnObject;

	// Constructor

	/**
	 * コンストラクタ<br>
	 * @param host LDAPホスト
	 * @param port LDAPポート
	 * @param version LDAPバージョン
	 * @param baseDn ベースDN
	 * @param anonymousBind 匿名接続フラグ
	 * @param sslConnection SSL接続フラグ
	 * @param sslSecurityLevel SSL接続セキュリティレベル
	 * @param userDn 接続ユーザーDN
	 * @param userPassword 接続パスワード
	 * @param searchScope 検索スコープ
	 * @param countLimit 検索数限界サイズ
	 * @param timeLimit 検索数タイムアウト
	 * @param derefLink 検索中のリンク間接参照フラグ
	 * @param returnObject 検索結果としてオブジェクトを返すフラグ
	 */
	public LDAPManagerConfig(String host, //
			int port, //
			int version, //
			String baseDn, //
			boolean anonymousBind, //
			boolean sslConnection, //
			String sslSecurityLevel, //
			String userDn, //
			String userPassword, //
			int searchScope, //
			int countLimit, //
			int timeLimit, //
			boolean derefLink, //
			boolean returnObject//
	) {
		super();
		this.host = host;
		this.port = port;
		this.version = version;
		this.baseDn = baseDn;
		this.anonymousBind = anonymousBind;
		this.sslConnection = sslConnection;
		this.sslSecurityLevel = sslSecurityLevel;
		this.userDn = userDn;
		this.userPassword = userPassword;
		this.searchScope = searchScope;
		this.countLimit = countLimit;
		this.timeLimit = timeLimit;
		this.derefLink = derefLink;
		this.returnObject = returnObject;
	}

	/**
	 * コンストラクタ<br>
	 * @param host LDAPホスト
	 * @param port LDAPポート
	 * @param version LDAPバージョン
	 * @param baseDn ベースDN
	 * @param userDn 接続ユーザーDN
	 * @param userPassword 接続パスワード
	 */
	public LDAPManagerConfig(String host, int port, int version, String baseDn, String userDn, String userPassword) {
		this(host, //
				port, //
				version, //
				baseDn, //
				DEFAULT_ANONYMOUS_BIND, //
				DEFAULT_SSL_BIND, //
				DEFAULT_SSL_SECURITY_LEVEL, //
				userDn, //
				userPassword, //
				DEFAULT_SEARCH_SCOPE, //
				DEFAULT_COUNT_LIMIT, //
				DEFAULT_TIME_LIMIT, //
				DEFAULT_DEREF_LINK, //
				DEFAULT_RETURN_OBJECT //
		);
	}

	/**
	 * コンストラクタ<br>
	 */
	public LDAPManagerConfig() {
		this(null, //
				DEFAULT_LDAP_PORT, //
				DEFAULT_LDAP_VERSION, //
				null, //
				DEFAULT_ANONYMOUS_BIND, //
				DEFAULT_SSL_BIND, //
				DEFAULT_SSL_SECURITY_LEVEL, //
				null, //
				null, //
				DEFAULT_SEARCH_SCOPE, //
				DEFAULT_COUNT_LIMIT, //
				DEFAULT_TIME_LIMIT, //
				DEFAULT_DEREF_LINK, //
				DEFAULT_RETURN_OBJECT //
		);
	}

	// Accessor Method

	/**
	 * LDAPホストを取得します。<br>
	 * @return LDAPホスト
	 */
	public String getHost() {
		return host;
	}

	/**
	 * LDAPホストを設定します。<br>
	 * @param host LDAPホスト
	 */
	public void setHost(String host) {
		this.host = host;
	}

	/**
	 * LDAPポートを取得します。<br>
	 * @return LDAPポート
	 */
	public int getPort() {
		return port;
	}

	/**
	 * LDAPポートを設定します。<br>
	 * @param port LDAPポート
	 */
	public void setPort(int port) {
		this.port = port;
	}

	/**
	 * LDAPバージョンを取得します。<br>
	 * @return LDAPバージョン
	 */
	public int getVersion() {
		return version;
	}

	/**
	 * LDAPバージョンを設定します。<br>
	 * @param version LDAPバージョン
	 */
	public void setVersion(int version) {
		this.version = version;
	}

	/**
	 * ベースDNを取得します。<br>
	 * @return ベースDN
	 */
	public String getBaseDn() {
		return baseDn;
	}

	/**
	 * ベースDNを設定します。<br>
	 * @param baseDn ベースDN
	 */
	public void setBaseDn(String baseDn) {
		this.baseDn = baseDn;
	}

	/**
	 * 匿名接続フラグを取得します。<br>
	 * @return 匿名接続フラグ
	 */
	public boolean isAnonymousBind() {
		return anonymousBind;
	}

	/**
	 * 匿名接続フラグを設定します。<br>
	 * @param anonymousBind 匿名接続フラグ
	 */
	public void setAnonymousBind(boolean anonymousBind) {
		this.anonymousBind = anonymousBind;
	}

	/**
	 * SSL接続フラグを取得します。<br>
	 * @return SSL接続フラグ
	 */
	public boolean isSslConnection() {
		return sslConnection;
	}

	/**
	 * SSL接続フラグを設定します。<br>
	 * @param sslConnection SSL接続フラグ
	 */
	public void setSslConnection(boolean sslConnection) {
		this.sslConnection = sslConnection;
	}

	/**
	 * SSL接続セキュリティレベルを取得します。<br>
	 * @return SSL接続セキュリティレベル
	 */
	public String getSslSecurityLevel() {
		return sslSecurityLevel;
	}

	/**
	 * SSL接続セキュリティレベルを設定します。<br>
	 * @param sslSecurityLevel SSL接続セキュリティレベル
	 */
	public void setSslSecurityLevel(String sslSecurityLevel) {
		this.sslSecurityLevel = sslSecurityLevel;
	}

	/**
	 * 接続ユーザーDNを取得します。<br>
	 * @return 接続ユーザーDN
	 */
	public String getUserDn() {
		return userDn;
	}

	/**
	 * 接続ユーザーDNを設定します。<br>
	 * @param userDn 接続ユーザーDN
	 */
	public void setUserDn(String userDn) {
		this.userDn = userDn;
	}

	/**
	 * 接続パスワードを取得します。<br>
	 * @return 接続パスワード
	 */
	public String getUserPassword() {
		return userPassword;
	}

	/**
	 * 接続パスワードを設定します。<br>
	 * @param userPassword 接続パスワード
	 */
	public void setUserPassword(String userPassword) {
		this.userPassword = userPassword;
	}

	/**
	 * 検索スコープを取得します。<br>
	 * @return 検索スコープ
	 */
	public int getSearchScope() {
		return searchScope;
	}

	/**
	 * 検索スコープを設定します。<br>
	 * @param searchScope 検索スコープ
	 */
	public void setSearchScope(int searchScope) {
		this.searchScope = searchScope;
	}

	/**
	 * 検索数限界サイズを取得します。<br>
	 * @return 検索数限界サイズ
	 */
	public int getCountLimit() {
		return countLimit;
	}

	/**
	 * 検索数限界サイズを設定します。<br>
	 * @param countLimit 検索数限界サイズ
	 */
	public void setCountLimit(int countLimit) {
		this.countLimit = countLimit;
	}

	/**
	 * 検索数タイムアウトを取得します。<br>
	 * @return 検索数タイムアウト
	 */
	public int getTimeLimit() {
		return timeLimit;
	}

	/**
	 * 検索数タイムアウトを設定します。<br>
	 * @param timeLimit 検索数タイムアウト
	 */
	public void setTimeLimit(int timeLimit) {
		this.timeLimit = timeLimit;
	}

	/**
	 * 検索中のリンク間接参照フラグを取得します。<br>
	 * @return 検索中のリンク間接参照フラグ
	 */
	public boolean isDerefLink() {
		return derefLink;
	}

	/**
	 * 検索中のリンク間接参照フラグを設定します。<br>
	 * @param derefLink 検索中のリンク間接参照フラグ
	 */
	public void setDerefLink(boolean derefLink) {
		this.derefLink = derefLink;
	}

	/**
	 * 検索結果としてオブジェクトを返すフラグを取得します。<br>
	 * @return 検索結果としてオブジェクトを返すフラグ
	 */
	public boolean isReturnObject() {
		return returnObject;
	}

	/**
	 * 検索結果としてオブジェクトを返すフラグを設定します。<br>
	 * @param returnObject 検索結果としてオブジェクトを返すフラグ
	 */
	public void setReturnObject(boolean returnObject) {
		this.returnObject = returnObject;
	}

	// Class Method

	/**
	 * LDAP接続用URLを生成します。<br>
	 * @return LDAP接続用URL
	 */
	public String createConnectionURL() {
		StringBuffer buffer = new StringBuffer();
		if (sslConnection) {
			buffer.append("ldaps://");
		} else {
			buffer.append("ldap://");
		}
		buffer.append(host);
		if (sslConnection) {
			buffer.append(port == DEFAULT_LDAP_PORT ? "" : ":").append(port);
		} else {
			buffer.append(port == DEFAULT_LDAP_SSL_PORT ? "" : ":").append(port);
		}
		return buffer.toString();
	}

	/**
	 * 定義されている環境変数からディレクトリコンテキストを生成します。<br>
	 * @return ディレクトリコンテキスト
	 * @throws NamingException 正常に変数からコンテキストオブジェクトを生成できなかった場合に発生
	 */
	public DirContext createContext() throws NamingException {
		Hashtable environment = new Hashtable();
		environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
		environment.put(Context.SECURITY_PRINCIPAL, userDn);
		environment.put(Context.SECURITY_CREDENTIALS, userPassword);
		environment.put(Context.PROVIDER_URL, createConnectionURL());
		environment.put("java.naming.ldap.version", String.valueOf(version));
		if (sslConnection) {
			environment.put(Context.SECURITY_AUTHENTICATION, sslSecurityLevel);
			environment.put(Context.SECURITY_PROTOCOL, "ssl");
		}
		return new InitialDirContext(environment);
	}

	/**
	 * 定義されている環境変数から検索コントロールオブジェクトを生成します。<br>
	 * @return 検索コントロールオブジェクト
	 */
	public SearchControls createSearchControls() {
		SearchControls controls = new SearchControls();
		controls.setSearchScope(searchScope);
		controls.setCountLimit(countLimit);
		controls.setTimeLimit(timeLimit);
		controls.setDerefLinkFlag(derefLink);
		controls.setReturningObjFlag(false);
		return controls;
	}

	// Override Method

	/**
	 * オブジェクトの文字列表現を返します。<br>
	 * @return オブジェクトの文字列表現
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("{");
		buffer.append("host=");
		buffer.append(host);
		buffer.append(",");
		buffer.append("port=");
		buffer.append(port);
		buffer.append(",");
		buffer.append("version=");
		buffer.append(version);
		buffer.append(",");
		buffer.append("baseDn=");
		buffer.append(baseDn);
		buffer.append(",");
		buffer.append("anonymousBind=");
		buffer.append(anonymousBind);
		buffer.append(",");
		buffer.append("sslConnection=");
		buffer.append(sslConnection);
		buffer.append(",");
		buffer.append("sslSecurityLevel=");
		buffer.append(sslSecurityLevel);
		buffer.append(",");
		buffer.append("userDn=");
		buffer.append(userDn);
		buffer.append(",");
		buffer.append("userPassword=");
		buffer.append(userPassword);
		buffer.append(",");
		buffer.append("searchScope=");
		buffer.append(searchScope);
		buffer.append(",");
		buffer.append("countLimit=");
		buffer.append(countLimit);
		buffer.append(",");
		buffer.append("timeLimit=");
		buffer.append(timeLimit);
		buffer.append(",");
		buffer.append("derefLink=");
		buffer.append(derefLink);
		buffer.append(",");
		buffer.append("returnObject=");
		buffer.append(returnObject);
		buffer.append("}");
		return buffer.toString();
	}
}

