/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2007 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.dbm.internal.mbean;

// JOnAS imports
import java.sql.SQLException;
import java.util.Map;

import org.ow2.jonas.dbm.internal.DBMConstants;
import org.ow2.jonas.dbm.internal.cm.ConnectionManager;
import org.ow2.jonas.dbm.internal.cm.Pool;
import org.ow2.jonas.lib.management.javaee.J2EEManagedObject;
import org.ow2.jonas.lib.management.reconfig.PropertiesConfigurationData;


/**
 * MBean class for JDBCDataSource Management.
 * This MBean manages a connection manager and its associated pool. This couple
 * represents the JOnAS data source implementation
 *
 * @author Eric Hardesty JSR 77 (J2EE Management Standard)
 * @author Adriana Danes add support for persistent reconfiguration
 */
public class JDBCDataSource extends J2EEManagedObject {

    /**
     * OBJECT_NAME of the associated JDBCDriver MBean
     */
    private String jdbcDriver = null;

    /**
     * Associated connection manager
     */
    private ConnectionManager cm = null;

    /**
     * Pool associated to the connection manager
     */
    private Pool pool = null;

    /**
     * The current data source name
     */
    private String datasourceName = null;

    /**
     * Value used as sequence number by reconfiguration notifications
     */
    private long sequenceNumber = 0;

    /**
     * @param objectName This MBean's OBJECT_NAME
     * @param cm Associated ConnectionManager reference
     */
    public JDBCDataSource(final String objectName, final ConnectionManager cm) {
        super(objectName);
        this.cm = cm;
        this.pool = cm.getPool();
        this.datasourceName = cm.getDatasourceName();
    }

    // JDBC DataSource Configuration

    /**
     * @return The OBJECT_NAME of the associated JDBCDriver MBean
     */
    public String getJdbcDriver() {
        return jdbcDriver;
    }

    /**
     * @param jdbcDriverObjectName OBJECT_NAME of the associated JDBCDriver MBean
     */
    public void setJdbcDriver(final String jdbcDriverObjectName) {
        jdbcDriver = jdbcDriverObjectName;
    }

    /**
     * Return the current data source name. Returns the local attribute value (optimize
     * call) - this value can't be changed, moreover, its used by private methods.
     * @return The current data source name
     */
    public String getName() {
        return datasourceName;
    }

    /**
     * @return The JNDI name
     */
    public String getJndiName() {
        return cm.getDSName();
    }

    /**
     * @return Description of this data source
     */
    public String getDescription() {
        return cm.getDataSourceDescription();
    }

    /**
     * @return The JDBC URL for the database
     */
    public String getUrl() {
        return cm.getUrl();
    }

    /**
     * @return The user name for connection to the database
     */
    public String getUserName() {
        return cm.getUserName();
    }

    /**
     * @return The password for connection to the database
     */
    public String getUserPassword() {
        return cm.getPassword();
    }

    /**
     * @return The mapper JORM for the database
     */
    public String getMapperName() {
        return cm.getMapperName();
    }

    // JDBC Connection Pool Configuration

    /**
     * @return JDBC connection checking level
     */
    public Integer getJdbcConnCheckLevel() {
        return new Integer(pool.getCheckLevel());
    }
    /**
     * Sets the JDBC connection checking level
     * @param level connection level
     */
    public void setJdbcConnCheckLevel(final Integer level) {
        pool.setCheckLevel(level.intValue());
        String propName = DBMConstants.CONNCHECKLEVEL;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, level.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return JDBC connections maximum age
     */
    public Integer getJdbcConnMaxAge() {
        return new Integer(pool.getMaxAge());
    }
    /**
     * @param mn JDBC connections maximum age
     */
    public void setJdbcConnMaxAge(final Integer mn) {
        pool.setMaxAge(mn.intValue());
        String propName = DBMConstants.CONNMAXAGE;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, mn.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return max maximum size of JDBC connection pool
     */
    public Integer getJdbcMaxConnPool() {
        return new Integer(pool.getPoolMax());
    }
    /**
     * @param max maximum size of JDBC connection pool
     */
    public void setJdbcMaxConnPool(final Integer max) {
        pool.setPoolMax(max.intValue());
        // Update the configuration file
        String propName = DBMConstants.MAXCONPOOL;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, max.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return maximum opening time of JDBC connections
     */
    public Integer getJdbcMaxOpenTime() {
        return new Integer(pool.getMaxOpenTime());
    }
    /**
     * @param mn maximum opening time in minutes for JDBC connections
     */
    public void setJdbcMaxOpenTime(final Integer mn) {
        pool.setMaxOpenTime(mn.intValue());
        String propName = DBMConstants.MAXOPENTIME;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, mn.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return maximum nb of waiters allowed
     */
    public Integer getJdbcMaxWaiters() {
        return new Integer(pool.getMaxWaiters());
    }
    /**
     * @param max maximum nb of waiters allowed
     */
    public void setJdbcMaxWaiters(final Integer max) {
        pool.setMaxWaiters(max.intValue());
        // Update the configuration file
        String propName = DBMConstants.MAXWAITERS;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, max.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return maximum time to wait for a connection, in seconds
     */
    public Integer getJdbcMaxWaitTime() {
        return new Integer(pool.getMaxWaitTime());
    }

    /**
     * @param max maximum time to wait for a connection, in seconds
     */
    public void setJdbcMaxWaitTime(final Integer max) {
        pool.setMaxWaitTime(max.intValue());
        // Update the configuration file
        String propName = DBMConstants.MAXWAITTIME;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, max.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return PreparedStatement cache size
     */
    public Integer getJdbcPstmtMax() {
        return new Integer(cm.getPstmtMax());
    }

    /**
     * @param max PreparedStatement cache size
     */
    public void setJdbcPstmtMax(final Integer max) {
        cm.setPstmtMax(max.intValue());
        // Update the configuration file
        String propName = DBMConstants.PSTMTMAX;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, max.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return minimum size of connection pool
     */
    public Integer getJdbcMinConnPool() {
        return new Integer(pool.getPoolMin());
    }
    /**
     * MBean method allowing to set the minimum size of connection pool
     * @param min minimum size of connection pool
     */
    public void setJdbcMinConnPool(final Integer min) {
        pool.setPoolMin(min.intValue());
        // Update the configuration file
        String propName = DBMConstants.MINCONPOOL;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, min.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return sampling period for refresching pool statistics
     */
    public Integer getJdbcSamplingPeriod() {
        return new Integer(pool.getSamplingPeriod());
    }

    /**
     * @param i sampling period for refresching pool statistics
     */
    public void setJdbcSamplingPeriod(final Integer i) {
        pool.setSamplingPeriod(i.intValue());
        // Update the configuration file
        String propName = DBMConstants.ADJUSTPERIOD;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, i.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return adjust period for pool limit recomputing (in sec.)
     */
    public Integer getJdbcAdjustPeriod() {
        return new Integer(pool.getAdjustPeriod());
    }

    /**
     * @param i adjust period for pool limit recomputing (in sec.)
     */
    public void setJdbcAdjustPeriod(final Integer i) {
        pool.setAdjustPeriod(i.intValue());
        // Update the configuration file
        String propName = DBMConstants.ADJUSTPERIOD;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, i.toString());
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
    }

    /**
     * @return SQL query for JDBC connections test
     */
    public String getJdbcTestStatement() {
        return pool.getTestStatement();
    }
    /**
     * @param test SQL query for JDBC connection test
     * @return the test statement if the test succeeded, an error message otherwise
     */
    public String setJdbcTestStatement(final String test) {
        String result;
        try {
            result = pool.checkConnection(test);
            if (result.equals(test)) {
                // Test succeeded
                pool.setTestStatement(test);
                result = test;
            }
        } catch (SQLException e) {
            result = e.toString();
        }
        String propName = DBMConstants.CONNTESTSTMT;
        PropertiesConfigurationData prop = new PropertiesConfigurationData(propName, test);
        // Send a reconfiguration notification to the listener MBean
        sendReconfigNotification(getSequenceNumber(), datasourceName, prop);
        return result;
    }

    // JDBC Connection Pool Statistics

    /**
     * @return number of connection failures
     */
    public Integer getConnectionFailures() {
        return new Integer(pool.getConnectionFailures());
    }
    /**
     * @return number of connection leaks
     */
    public Integer getConnectionLeaks() {
        return new Integer(pool.getConnectionLeaks());
    }
    /**
     * @return number of busy connections
     */
    public Integer getCurrentBusy() {
        return new Integer(pool.getCurrentBusy());
    }
    /**
     * @return number of busy connections
     */
    public Integer getBusyMax() {
        return new Integer(pool.getBusyMaxRecent());
    }
    /**
     * @return number of busy connections
     */
    public Integer getBusyMin() {
        return new Integer(pool.getBusyMinRecent());
    }
    /**
     * @return number of connections used in transactions
     */
    public Integer getCurrentInTx() {
        return new Integer(pool.getCurrentInTx());
    }
    /**
     * @return number of opened connections
     */
    public Integer getCurrentOpened() {
        return new Integer(pool.getCurrentOpened());
    }
    /**
     * @return current number of connection waiters
     */
    public Integer getCurrentWaiters() {
        return new Integer(pool.getCurrentWaiters());
    }
    /**
     * @return number of opened physical JDBC connections
     */
    public Integer getOpenedCount() {
        return new Integer(pool.getOpenedCount());
    }
    /**
     * @return number of open calls that were rejected because too many waiters
     */
    public Integer getRejectedFull() {
        return new Integer(pool.getRejectedFull());
    }
    /**
     * @return total number of open calls that were rejected
     */
    public Integer getRejectedOpen() {
        return new Integer(pool.getRejectedOpen());
    }
    /**
     * @return number of open calls that were rejected by an unknown reason
     */
    public Integer getRejectedOther() {
        return new Integer(pool.getRejectedOther());
    }
    /**
     * @return number of open calls that were rejected by timeout
     */
    public Integer getRejectedTimeout() {
        return new Integer(pool.getRejectedTimeout());
    }
    /**
     * @return number of xa connection served
     */
    public Integer getServedOpen() {
        return new Integer(pool.getServedOpen());
    }
    /**
     * @return total number of waiters since datasource creation.
     */
    public Integer getWaiterCount() {
        return new Integer(pool.getWaiterCount());
    }
    /**
     * @return Maximum number of waiters since datasource creation.
     */
    public Integer getWaitersHigh() {
        return new Integer(pool.getWaitersHigh());
    }
    /**
     * @return Maximum nb of waiters in last sampling period
     */
    public Integer getWaitersHighRecent() {
        return new Integer(pool.getWaitersHighRecent());
    }
    /**
     * @return Maximum waiting time (millisec) since datasource creation.
     */
    public Long getWaitingHigh() {
        return new Long(pool.getWaitingHigh());
    }
    /**
     * @return Maximum waiting time (millisec) in last sampling period
     */
    public Long getWaitingHighRecent() {
        return new Long(pool.getWaitingHighRecent());
    }
    /**
     * @return Total waiting time (millisec) since datasource creation.
     */
    public Long getWaitingTime() {
        return new Long(pool.getWaitingTime());
    }

    public int[] getOpenedConnections(final int seconds) {
        return cm.getOpenedConnections(seconds);
    }

    public int[] getOpenedConnections() {
        return cm.getOpenedConnections();
    }

    public Map getConnectionDetails(final int connectionId) {
        return cm.getConnectionDetails(connectionId);
    }

    public void forceCloseConnection(final int connectionId) {
        cm.forceCloseConnection(connectionId);
    }

    public void setObservable(final boolean observable) {
    	cm.setObservable(observable);
    }

    public boolean isObservable() {
    	return cm.isObservable();
    }

    /**
     * Gets the sequence number for reconfiguration opeartions
     * @return the sequence number for reconfiguration operations
     */
    protected long getSequenceNumber() {
        return ++sequenceNumber;
    }

    /**
     * save updated configuration
     */
    public void saveConfig() {
        sendSaveNotification(getSequenceNumber(), datasourceName);
    }
}
