/*
 * Copyright 2004-2014 the Seasar Foundation and the Others.
 *
 * 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.seasar.extension.dbcp.impl;

import java.io.PrintWriter;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;

import javax.sql.XAConnection;
import javax.sql.XADataSource;

/**
 * {@link XADataSource}の実装です。
 *
 * @author higa
 *
 */
public class XADataSourceImpl implements XADataSource {

    /** プロパティ */
    private final Properties properties = new Properties();

    /** ドライバクラス名 */
    private String driverClassName;
    /** URL */
    private String url;
    /** ユーザー */
    private String user;
    /** パスワード */
    private String password;
    /** ログインタイムアウト */
    private int loginTimeout;

    /**
     * {@link XADataSourceImpl}を作成します。
     */
    public XADataSourceImpl() {
        super();
    }

    /**
     * ドライバクラス名を返します。
     *
     * @return ドライバクラス名
     */
    public String getDriverClassName() {
        return this.driverClassName;
    }

    /**
     * ドライバクラス名を設定します。
     *
     * @param className ドライバクラス名
     */
    public void setDriverClassName(final String className) {
        this.driverClassName = className;
        if (className != null && !className.isEmpty()) {
            registerDriver(className);
        }
    }

    /**
     * JDBCドライバを登録します。
     *
     * @param className 登録するJDBCドライバクラス名
     * @since 2.4.10
     */
    public static void registerDriver(final String className) {
        final ClassLoader loader = Thread.currentThread().getContextClassLoader();
        try {
            final Class<? extends Driver> cls =
                    Class.forName(className, true, loader).asSubclass(Driver.class);
            final Driver driver = cls.getDeclaredConstructor().newInstance();
            DriverManager.registerDriver(driver);
        } catch (final ReflectiveOperationException | SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * URLを返します。
     *
     * @return URL
     */
    public String getURL() {
        return this.url;
    }

    /**
     * URLを設定します。
     *
     * @param val URL
     */
    public void setURL(final String val) {
        this.url = val;
    }

    /**
     * ユーザを返します。
     *
     * @return ユーザ
     */
    public String getUser() {
        return this.user;
    }

    /**
     * ユーザを設定します。
     *
     * @param val ユーザ
     */
    public void setUser(final String val) {
        this.user = val;
    }

    /**
     * パスワードを返します。
     *
     * @return パスワード
     */
    public String getPassword() {
        return this.password;
    }

    /**
     * パスワードを設定します。
     *
     * @param val パスワード
     */
    public void setPassword(final String val) {
        this.password = val;
    }

    /**
     * プロパティを追加します。
     *
     * @param name プロパティ名
     * @param value 値
     */
    public void addProperty(final String name, final String value) {
        this.properties.put(name, value);
    }

    /**
     * @see javax.sql.XADataSource#getXAConnection()
     */
    @Override
    public XAConnection getXAConnection() throws SQLException {
        return getXAConnection(this.user, this.password);
    }

    /**
     * @see javax.sql.XADataSource#getXAConnection(java.lang.String, java.lang.String)
     */
    @Override
    public XAConnection getXAConnection(final String usr, final String pwd) throws SQLException {
        final Properties info = new Properties();
        info.putAll(this.properties);
        if (usr != null && !usr.isEmpty()) {
            info.put("user", usr);
        }
        if (pwd != null && !pwd.isEmpty()) {
            info.put("password", pwd);
        }

        final int currentLoginTimeout = DriverManager.getLoginTimeout();
        try {
            DriverManager.setLoginTimeout(this.loginTimeout);
            return new XAConnectionImpl(DriverManager.getConnection(this.url, info));
        } finally {
            DriverManager.setLoginTimeout(currentLoginTimeout);
        }
    }

    /**
     * @see javax.sql.CommonDataSource#getLogWriter()
     */
    @Override
    public PrintWriter getLogWriter() {
        return null;
    }

    /**
     * @see javax.sql.CommonDataSource#setLogWriter(java.io.PrintWriter)
     */
    @Override
    public void setLogWriter(final PrintWriter logWriter) {
        return;
    }

    /**
     * @see javax.sql.CommonDataSource#getLoginTimeout()
     */
    @Override
    public int getLoginTimeout() {
        return this.loginTimeout;
    }

    /**
     * @see javax.sql.CommonDataSource#setLoginTimeout(int)
     */
    @Override
    public void setLoginTimeout(final int timeout) {
        this.loginTimeout = timeout;
    }

    /**
     * @see javax.sql.CommonDataSource#getParentLogger()
     */
    @Override
    public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
        throw new SQLFeatureNotSupportedException();
    }
}
