/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.gjc.spi;

import javax.resource.ResourceException;
import javax.resource.spi.ConnectionRequestInfo;

import com.sun.gjc.util.SecurityUtils;

import javax.resource.spi.security.PasswordCredential;

import com.sun.gjc.common.DataSourceSpec;
import com.sun.gjc.common.DataSourceObjectBuilder;

import java.sql.Connection;
import javax.sql.PooledConnection;

import com.sun.logging.*;

import java.util.logging.Logger;
import java.util.logging.Level;

import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;

/**
 * <code>ManagedConnectionFactory</code> implementation for Generic JDBC Connector.
 * This class is extended by the DataSource specific <code>ManagedConnection</code> factories
 * and the <code>ManagedConnectionFactory</code> for the <code>DriverManager</code>.
 *
 * @author Evani Sai Surya Kiran, Aditya Gore
 * @version 1.0, 02/08/03
 */

public abstract class ManagedConnectionFactory implements javax.resource.spi.ManagedConnectionFactory,
        javax.resource.spi.ValidatingManagedConnectionFactory, java.io.Serializable {

    protected DataSourceSpec spec = new DataSourceSpec();
    protected transient DataSourceObjectBuilder dsObjBuilder;

    protected java.io.PrintWriter logWriter = null;
    protected javax.resource.spi.ResourceAdapter ra = null;

    private static Logger _logger;
    protected boolean statementWrapping;

    private JdbcObjectsFactory jdbcObjectsFactory = JdbcObjectsFactory.getInstance();

    static {
        _logger = LogDomains.getLogger(LogDomains.RSR_LOGGER);
    }

    protected javax.resource.spi.LazyEnlistableConnectionManager cm_;
    protected boolean isLazyCm_;

    /**
     * Creates a Connection Factory instance. The <code>ConnectionManager</code> implementation
     * of the resource adapter is used here.
     *
     * @return Generic JDBC Connector implementation of <code>javax.sql.DataSource</code>
     */
    public Object createConnectionFactory() {
        if (logWriter != null) {
            logWriter.println("In createConnectionFactory()");
        }
        return jdbcObjectsFactory.getDataSourceInstance(this, null);
    }

    /**
     * Creates a Connection Factory instance. The <code>ConnectionManager</code> implementation
     * of the application server is used here.
     *
     * @param cxManager <code>ConnectionManager</code> passed by the application server
     * @return Generic JDBC Connector implementation of <code>javax.sql.DataSource</code>
     */
    public Object createConnectionFactory(javax.resource.spi.ConnectionManager cxManager) {
        if (logWriter != null) {
            logWriter.println("In createConnectionFactory(javax.resource.spi.ConnectionManager cxManager)");
        }

        javax.sql.DataSource cf = jdbcObjectsFactory.getDataSourceInstance(this, cxManager);

        if (cxManager instanceof javax.resource.spi.LazyEnlistableConnectionManager) {
            cm_ = (javax.resource.spi.LazyEnlistableConnectionManager) cxManager;
            isLazyCm_ = true;
        }
        return cf;
    }

    /**
     * Creates a new physical connection to the underlying EIS resource
     * manager.
     *
     * @param subject       <code>Subject</code> instance passed by the application server
     * @param cxRequestInfo <code>ConnectionRequestInfo</code> which may be created
     *                      as a result of the invocation <code>getConnection(user, password)</code>
     *                      on the <code>DataSource</code> object
     * @return <code>ManagedConnection</code> object created
     * @throws ResourceException if there is an error in instantiating the
     *                           <code>DataSource</code> object used for the
     *                           creation of the <code>ManagedConnection</code> object
     * @throws SecurityException if there ino <code>PasswordCredential</code> object
     *                           satisfying this request
     * @throws ResourceException if there is an error in allocating the
     *                           physical connection
     */
    public abstract javax.resource.spi.ManagedConnection createManagedConnection
            (javax.security.auth.Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException;

    /**
     * Check if this <code>ManagedConnectionFactory</code> is equal to
     * another <code>ManagedConnectionFactory</code>.
     *
     * @param other <code>ManagedConnectionFactory</code> object for checking equality with
     * @return true    if the property sets of both the
     *         <code>ManagedConnectionFactory</code> objects are the same
     *         false	otherwise
     */
    public abstract boolean equals(Object other);

    /**
     * Get the log writer for this <code>ManagedConnectionFactory</code> instance.
     *
     * @return <code>PrintWriter</code> associated with this <code>ManagedConnectionFactory</code> instance
     * @see <code>setLogWriter</code>
     */
    public java.io.PrintWriter getLogWriter() {
        return logWriter;
    }

    /**
     * Get the <code>ResourceAdapter</code> for this <code>ManagedConnectionFactory</code> instance.
     *
     * @return <code>ResourceAdapter</code> associated with this <code>ManagedConnectionFactory</code> instance
     * @see <code>setResourceAdapter</code>
     */
    public javax.resource.spi.ResourceAdapter getResourceAdapter() {
        if (logWriter != null) {
            logWriter.println("In getResourceAdapter");
        }
        return ra;
    }

    /**
     * Returns the hash code for this <code>ManagedConnectionFactory</code>.
     *
     * @return hash code for this <code>ManagedConnectionFactory</code>
     */
    public int hashCode() {
        if (logWriter != null) {
            logWriter.println("In hashCode");
        }
        return spec.hashCode();
    }

    /**
     * Returns a matched <code>ManagedConnection</code> from the candidate
     * set of <code>ManagedConnection</code> objects.
     *
     * @param connectionSet <code>Set</code> of  <code>ManagedConnection</code>
     *                      objects passed by the application server
     * @param subject       passed by the application server
     *                      for retrieving information required for matching
     * @param cxRequestInfo <code>ConnectionRequestInfo</code> passed by the application server
     *                      for retrieving information required for matching
     * @return <code>ManagedConnection</code> that is the best match satisfying this request
     * @throws ResourceException if there is an error accessing the <code>Subject</code>
     *                           parameter or the <code>Set</code> of <code>ManagedConnection</code>
     *                           objects passed by the application server
     */
    public javax.resource.spi.ManagedConnection matchManagedConnections(
            java.util.Set connectionSet, javax.security.auth.Subject subject, ConnectionRequestInfo cxRequestInfo)
            throws ResourceException {
        if (logWriter != null) {
            logWriter.println("In matchManagedConnections");
        }

        if (connectionSet == null) {
            return null;
        }



        PasswordCredential pc = SecurityUtils.getPasswordCredential(this, subject, cxRequestInfo);

        java.util.Iterator iter = connectionSet.iterator();
        com.sun.gjc.spi.ManagedConnection mc = null;

        while (iter.hasNext()) {
            try {
                mc = (com.sun.gjc.spi.ManagedConnection) iter.next();
            } catch (java.util.NoSuchElementException nsee) {
                _logger.log(Level.SEVERE, "jdbc.exc_iter");
                throw new ResourceException(nsee.getMessage());
            }
            if (pc == null && this.equals(mc.getManagedConnectionFactory())) {
                return mc;
            } else if (SecurityUtils.isPasswordCredentialEqual(pc, mc.getPasswordCredential())) {
                return mc;
            }
        }
        return null;
    }

    /**
     * This method returns a set of invalid <code>ManagedConnection</code>
     * objects chosen from a specified set of <code>ManagedConnection</code>
     * objects.
     *
     * @param connectionSet a set of <code>ManagedConnection</code> objects
     *                      that need to be validated.
     * @return a set of invalid <code>ManagedConnection</code> objects.
     * @throws ResourceException generic exception.
     */
    public Set getInvalidConnections(Set connectionSet) throws ResourceException {
        Iterator iter = connectionSet.iterator();
        Set invalid = new HashSet();
        while (iter.hasNext()) {
            ManagedConnection mc = (ManagedConnection) iter.next();
            try {
                isValid(mc);
            } catch (ResourceException re) {
                invalid.add(mc);
                mc.connectionErrorOccurred(re, null);
                if(_logger.isLoggable(Level.FINE)){
                    _logger.log(Level.FINE, "jdbc.invalid_connection", re);
                }
            }
        }
        return invalid;
    }

    //GJCINT

    /**
     * Checks if a <code>ManagedConnection</code> is to be validated or not
     * and validates it or returns.
     *
     * @param mc <code>ManagedConnection</code> to be validated
     * @throws ResourceException if the connection is not valid or
     *                           if validation method is not proper
     */
    void isValid(com.sun.gjc.spi.ManagedConnection mc) throws ResourceException {

        if (mc == null || mc.isTransactionInProgress()) {
            return;
        }

        String conVal = spec.getDetail(DataSourceSpec.CONNECTIONVALIDATIONREQUIRED);

        boolean connectionValidationRequired =
                (conVal == null) ? false : Boolean.valueOf(conVal.toLowerCase());
        if (!connectionValidationRequired) {
            return;
        }


        String validationMethod = spec.getDetail(DataSourceSpec.VALIDATIONMETHOD).toLowerCase();

        mc.checkIfValid();
        /**
         * The above call checks if the actual physical connection
         * is usable or not.
         */
        java.sql.Connection con = mc.getActualConnection();

        if (validationMethod.equals("auto-commit")) {
            isValidByAutoCommit(con);
        } else if (validationMethod.equals("meta-data")) {
            isValidByMetaData(con);
        } else if (validationMethod.equals("table")) {
            isValidByTableQuery(con, spec.getDetail(DataSourceSpec.VALIDATIONTABLENAME));
        } else {
            throw new ResourceException("The validation method is not proper");
        }
    }

    /**
     * Checks if a <code>java.sql.Connection</code> is valid or not
     * by checking its auto commit property.
     *
     * @param con <code>java.sql.Connection</code> to be validated
     * @throws ResourceException if the connection is not valid
     */
    protected void isValidByAutoCommit(java.sql.Connection con) throws ResourceException {
        if (con == null) {
            throw new ResourceException("The connection is not valid as "
                    + "the connection is null");
        }

        try {
            // Notice that using something like
            // dbCon.setAutoCommit(dbCon.getAutoCommit()) will cause problems with
            // some drivers like sybase
            // We do not validate connections that are already enlisted
            //in a transaction
            // We cycle autocommit to true and false to by-pass drivers that
            // might cache the call to set autocomitt
            // Also notice that some XA data sources will throw and exception if
            // you try to call setAutoCommit, for them this method is not recommended

            boolean ac = con.getAutoCommit();
            if (ac) {
                con.setAutoCommit(false);
            } else {
                con.rollback(); // prevents uncompleted transaction exceptions
                con.setAutoCommit(true);
            }

            con.setAutoCommit(ac);

        } catch (Exception sqle) {
            _logger.log(Level.INFO, "jdbc.exc_autocommit_validation");
            throw new ResourceException(sqle);
        }
    }

    /**
     * Checks if a <code>java.sql.Connection</code> is valid or not
     * by checking its meta data.
     *
     * @param con <code>java.sql.Connection</code> to be validated
     * @throws ResourceException if the connection is not valid
     */
    protected void isValidByMetaData(java.sql.Connection con) throws ResourceException {
        if (con == null) {
            throw new ResourceException("The connection is not valid as "
                    + "the connection is null");
        }

        try {
            con.getMetaData();
        } catch (Exception sqle) {
            _logger.log(Level.INFO, "jdbc.exc_metadata_validation");
            throw new ResourceException(sqle);
        }                                                      
    }

    /**
     * Checks if a <code>java.sql.Connection</code> is valid or not
     * by querying a table.
     *
     * @param con       <code>java.sql.Connection</code> to be validated
     * @param tableName table which should be queried
     * @throws ResourceException if the connection is not valid
     */
    protected void isValidByTableQuery(java.sql.Connection con,
                                       String tableName) throws ResourceException {
        if (con == null) {
            throw new ResourceException("The connection is not valid as "
                    + "the connection is null");
        }

        java.sql.PreparedStatement stmt = null;
        java.sql.ResultSet rs = null;
        try {
            stmt = con.prepareStatement("SELECT COUNT(*) FROM " + tableName);
            rs = stmt.executeQuery();
        } catch (Exception sqle) {
                _logger.log(Level.INFO, "jdbc.exc_table_validation", tableName);
                throw new ResourceException(sqle);
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (Exception e1) {
            }

            try {
                if (stmt != null) {
                    stmt.close();
                }
            } catch (Exception e2) {
            }
        }
    }

    /**
     * Sets the isolation level specified in the <code>ConnectionRequestInfo</code>
     * for the <code>ManagedConnection</code> passed.
     *
     * @param mc <code>ManagedConnection</code>
     * @throws ResourceException if the isolation property is invalid
     *                           or if the isolation cannot be set over the connection
     */
    protected void setIsolation(com.sun.gjc.spi.ManagedConnection mc) throws ResourceException {

        java.sql.Connection con = mc.getActualConnection();
        if (con == null) {
            return;
        }

        String tranIsolation = spec.getDetail(DataSourceSpec.TRANSACTIONISOLATION);
        if (tranIsolation != null && tranIsolation.equals("") == false) {
            int tranIsolationInt = getTransactionIsolationInt(tranIsolation);
            try {
                con.setTransactionIsolation(tranIsolationInt);
                mc.setLastTransactionIsolationLevel(tranIsolationInt);
            } catch (java.sql.SQLException sqle) {
                _logger.log(Level.SEVERE, "jdbc.exc_tx_iso", sqle);
                throw new ResourceException("The transaction isolation could "
                        + "not be set: " + sqle.getMessage());
            }
        }
    }

    /**
     * Resets the isolation level for the <code>ManagedConnection</code> passed.
     * If the transaction level is to be guaranteed to be the same as the one
     * present when this <code>ManagedConnection</code> was created, as specified
     * by the <code>ConnectionRequestInfo</code> passed, it sets the transaction
     * isolation level from the <code>ConnectionRequestInfo</code> passed. Else,
     * it sets it to the transaction isolation passed.
     *
     * @param mc       <code>ManagedConnection</code>
     * @param tranIsol int
     * @throws ResourceException if the isolation property is invalid
     *                           or if the isolation cannot be set over the connection
     */
    void resetIsolation(com.sun.gjc.spi.ManagedConnection mc, int tranIsol) throws ResourceException {

        java.sql.Connection con = mc.getActualConnection();
        if (con == null) {
            return;
        }

        String tranIsolation = spec.getDetail(DataSourceSpec.TRANSACTIONISOLATION);
        if (tranIsolation != null && tranIsolation.equals("") == false) {
            String guaranteeIsolationLevel = spec.getDetail(DataSourceSpec.GUARANTEEISOLATIONLEVEL);

            if (guaranteeIsolationLevel != null && guaranteeIsolationLevel.equals("") == false) {
                boolean guarantee = Boolean.valueOf(guaranteeIsolationLevel.toLowerCase());

                if (guarantee) {
                    int tranIsolationInt = getTransactionIsolationInt(tranIsolation);
                    try {
                        if (tranIsolationInt != con.getTransactionIsolation()) {
                            con.setTransactionIsolation(tranIsolationInt);
                        }
                    } catch (java.sql.SQLException sqle) {
                        _logger.log(Level.SEVERE, "jdbc.exc_tx_iso", sqle);
                        throw new ResourceException("The isolation level could not be set: "
                                + sqle.getMessage());
                    }
                } else {
                    try {
                        if (tranIsol != con.getTransactionIsolation()) {
                            con.setTransactionIsolation(tranIsol);
                        }
                    } catch (java.sql.SQLException sqle) {
                        _logger.log(Level.SEVERE, "jdbc.exc_tx_iso", sqle);
                        throw new ResourceException("The isolation level could not be set: "
                                + sqle.getMessage());
                    }
                }
            }
        }
    }

    /**
     * Gets the integer equivalent of the string specifying
     * the transaction isolation.
     *
     * @param tranIsolation string specifying the isolation level
     * @return tranIsolationInt    the <code>java.sql.Connection</code> constant
     *         for the string specifying the isolation.
     */
    private int getTransactionIsolationInt(String tranIsolation) throws ResourceException {
        if (tranIsolation.equalsIgnoreCase("read-uncommitted")) {
            return java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
        } else if (tranIsolation.equalsIgnoreCase("read-committed")) {
            return java.sql.Connection.TRANSACTION_READ_COMMITTED;
        } else if (tranIsolation.equalsIgnoreCase("repeatable-read")) {
            return java.sql.Connection.TRANSACTION_REPEATABLE_READ;
        } else if (tranIsolation.equalsIgnoreCase("serializable")) {
            return java.sql.Connection.TRANSACTION_SERIALIZABLE;
        } else {
            throw new ResourceException("Invalid transaction isolation; the transaction "
                    + "isolation level can be empty or any of the following: "
                    + "read-uncommitted, read-committed, repeatable-read, serializable");
        }
    }

    /**
     * Common operation performed by all the child MCFs before returning a created mc
     */
    protected void validateAndSetIsolation(ManagedConnection mc) throws ResourceException {
        isValid(mc);
        setIsolation(mc);
    }

    /**
     * Set the log writer for this <code>ManagedConnectionFactory</code> instance.
     *
     * @param out <code>PrintWriter</code> passed by the application server
     * @see <code>getLogWriter</code>
     */
    public void setLogWriter(java.io.PrintWriter out) {
        logWriter = out;
    }

    /**
     * Set the associated <code>ResourceAdapter</code> JavaBean.
     *
     * @param ra <code>ResourceAdapter</code> associated with this
     *           <code>ManagedConnectionFactory</code> instance
     * @see <code>getResourceAdapter</code>
     */
    public void setResourceAdapter(javax.resource.spi.ResourceAdapter ra) {
        this.ra = ra;
    }

    /**
     * Sets the user name
     *
     * @param user <code>String</code>
     */
    public void setUser(String user) {
        spec.setDetail(DataSourceSpec.USERNAME, user);
    }

    /**
     * Gets the user name
     *
     * @return user
     */
    public String getUser() {
        return spec.getDetail(DataSourceSpec.USERNAME);
    }

    /**
     * Sets the password
     *
     * @param passwd <code>String</code>
     */
    public void setPassword(String passwd) {
        spec.setDetail(DataSourceSpec.PASSWORD, passwd);
    }

    /**
     * Gets the password
     *
     * @return passwd
     */
    public String getPassword() {
        return spec.getDetail(DataSourceSpec.PASSWORD);
    }

    /**
     * Sets the class name of the data source
     *
     * @param className <code>String</code>
     */
    public void setClassName(String className) {
        spec.setDetail(DataSourceSpec.CLASSNAME, className);
    }

    /**
     * Gets the class name of the data source
     *
     * @return className
     */
    public String getClassName() {
        return spec.getDetail(DataSourceSpec.CLASSNAME);
    }

    /**
     * Sets if connection validation is required or not
     *
     * @param conVldReq <code>String</code>
     */
    public void setConnectionValidationRequired(String conVldReq) {
        spec.setDetail(DataSourceSpec.CONNECTIONVALIDATIONREQUIRED, conVldReq);
    }

    /**
     * Returns if connection validation is required or not
     *
     * @return connection validation requirement
     */
    public String getConnectionValidationRequired() {
        return spec.getDetail(DataSourceSpec.CONNECTIONVALIDATIONREQUIRED);
    }

    /**
     * Sets the validation method required
     *
     * @param validationMethod <code>String</code>
     */
    public void setValidationMethod(String validationMethod) {
        spec.setDetail(DataSourceSpec.VALIDATIONMETHOD, validationMethod);
    }

    /**
     * Returns the connection validation method type
     *
     * @return validation method
     */
    public String getValidationMethod() {
        return spec.getDetail(DataSourceSpec.VALIDATIONMETHOD);
    }

    /**
     * Sets the table checked for during validation
     *
     * @param table <code>String</code>
     */
    public void setValidationTableName(String table) {
        spec.setDetail(DataSourceSpec.VALIDATIONTABLENAME, table);
    }

    /**
     * Returns the table checked for during validation
     *
     * @return table
     */
    public String getValidationTableName() {
        return spec.getDetail(DataSourceSpec.VALIDATIONTABLENAME);
    }

    /**
     * Sets the transaction isolation level
     *
     * @param trnIsolation <code>String</code>
     */
    public void setTransactionIsolation(String trnIsolation) {
        spec.setDetail(DataSourceSpec.TRANSACTIONISOLATION, trnIsolation);
    }

    /**
     * Returns the transaction isolation level
     *
     * @return transaction isolation level
     */
    public String getTransactionIsolation() {
        return spec.getDetail(DataSourceSpec.TRANSACTIONISOLATION);
    }

    /**
     * Sets if the transaction isolation level is to be guaranteed
     *
     * @param guaranteeIsolation <code>String</code>
     */
    public void setGuaranteeIsolationLevel(String guaranteeIsolation) {
        spec.setDetail(DataSourceSpec.GUARANTEEISOLATIONLEVEL, guaranteeIsolation);
    }

    /**
     * Returns the transaction isolation level
     *
     * @return isolation level guarantee
     */
    public String getGuaranteeIsolationLevel() {
        return spec.getDetail(DataSourceSpec.GUARANTEEISOLATIONLEVEL);
    }

    protected boolean isEqual(PasswordCredential pc, String user,
                              String password) {

        //if equal get direct connection else
        //get connection with user and password.

        String thisUser = (pc == null) ? null : pc.getUserName();
        char[] passwordArray = (pc == null) ? null : pc.getPassword();
        String thisPassword = (passwordArray == null) ? null : new String(passwordArray);

        return (isStringEqual(thisUser, user) && isStringEqual(thisPassword, password));

    }

    private boolean isStringEqual(String str1, String str2) {
        return str1 == null ? str2 == null : str1.equals(str2);
    }

    /**
     * Sets the server name.
     *
     * @param serverName <code>String</code>
     * @see <code>getServerName</code>
     */
    public void setServerName(String serverName) {
        spec.setDetail(DataSourceSpec.SERVERNAME, serverName);
    }

    /**
     * Gets the server name.
     *
     * @return serverName
     * @see <code>setServerName</code>
     */
    public String getServerName() {
        return spec.getDetail(DataSourceSpec.SERVERNAME);
    }

    /**
     * Sets the port number.
     *
     * @param portNumber <code>String</code>
     * @see <code>getPortNumber</code>
     */
    public void setPortNumber(String portNumber) {
        spec.setDetail(DataSourceSpec.PORTNUMBER, portNumber);
    }

    /**
     * Gets the port number.
     *
     * @return portNumber
     * @see <code>setPortNumber</code>
     */
    public String getPortNumber() {
        return spec.getDetail(DataSourceSpec.PORTNUMBER);
    }

    public void setJdbc30DataSource(String booleanValue){
        spec.setDetail(DataSourceSpec.JDBC30DATASOURCE,booleanValue);
    }

    public String getJdbc30DataSource(){
        return spec.getDetail(DataSourceSpec.JDBC30DATASOURCE);
    }

      /**
     * Sets the database name.
     *
     * @param databaseName <code>String</code>
     * @see <code>getDatabaseName</code>
     */
    public void setDatabaseName(String databaseName) {
        spec.setDetail(DataSourceSpec.DATABASENAME, databaseName);
    }

    /**
     * Gets the database name.
     *
     * @return databaseName
     * @see <code>setDatabaseName</code>
     */
    public String getDatabaseName() {
        return spec.getDetail(DataSourceSpec.DATABASENAME);
    }

      /**
     * Sets the data source name.
     *
     * @param dsn <code>String</code>
     * @see <code>getDataSourceName</code>
     */
    public void setDataSourceName(String dsn) {
        spec.setDetail(DataSourceSpec.DATASOURCENAME, dsn);
    }

    /**
     * Gets the data source name.
     *
     * @return dsn
     * @see <code>setDataSourceName</code>
     */
    public String getDataSourceName() {
        return spec.getDetail(DataSourceSpec.DATASOURCENAME);
    }

    /**
     * Set Statement Wrapping value
     * @param wrapping <code>String</code>
     * @see <code> getStatementWrapping </code>
     */
    public void setStatementWrapping(String wrapping) {
        spec.setDetail(DataSourceSpec.STATEMENTWRAPPING, wrapping);
        computeStatementWrappingStatus();
    }
    /**
     * Gets the statement wrapping value
     *
     * @return String representing "true" or "false"
     * @see <code>setStatementWrapping</code>
     */
    public String getStatementWrapping() {
        return spec.getDetail(DataSourceSpec.STATEMENTWRAPPING);
    }

    /**
     * Set StatementTimeout value
     * @param timeout <code>String</code>
     * @see <code> getStatementTimeout </code>
     */
    public void setStatementTimeout(String timeout) {
        spec.setDetail(DataSourceSpec.STATEMENTTIMEOUT, timeout);
    }

    /**
     * Gets the StatementTimeout value
     *
     * @return String representing "true" or "false"
     * @see <code>setStatementTimeout</code>
     */
    public String getStatementTimeout() {
        return spec.getDetail(DataSourceSpec.STATEMENTTIMEOUT);
    }

      /**
     * Sets the description.
     *
     * @param desc <code>String</code>
     * @see <code>getDescription</code>
     */
    public void setDescription(String desc) {
        spec.setDetail(DataSourceSpec.DESCRIPTION, desc);
    }

    /**
     * Gets the description.
     *
     * @return desc
     * @see <code>setDescription</code>
     */
    public String getDescription() {
        return spec.getDetail(DataSourceSpec.DESCRIPTION);
    }

       /**
     * Sets the network protocol.
     *
     * @param nwProtocol <code>String</code>
     * @see <code>getNetworkProtocol</code>
     */
    public void setNetworkProtocol(String nwProtocol) {
        spec.setDetail(DataSourceSpec.NETWORKPROTOCOL, nwProtocol);
    }

    /**
     * Gets the network protocol.
     *
     * @return nwProtocol
     * @see <code>setNetworkProtocol</code>
     */
    public String getNetworkProtocol() {
        return spec.getDetail(DataSourceSpec.NETWORKPROTOCOL);
    }

     /**
     * Sets the role name.
     *
     * @param roleName <code>String</code>
     * @see <code>getRoleName</code>
     */
    public void setRoleName(String roleName) {
        spec.setDetail(DataSourceSpec.ROLENAME, roleName);
    }

    /**
     * Gets the role name.
     *
     * @return roleName
     * @see <code>setRoleName</code>
     */
    public String getRoleName() {
        return spec.getDetail(DataSourceSpec.ROLENAME);
    }

            /**
     * Sets the login timeout.
     *
     * @param loginTimeOut <code>String</code>
     * @see <code>getLoginTimeOut</code>
     */
    public void setLoginTimeOut(String loginTimeOut) {
        spec.setDetail(DataSourceSpec.LOGINTIMEOUT, loginTimeOut);
    }

    /**
     * Gets the login timeout.
     *
     * @return loginTimeout
     * @see <code>setLoginTimeOut</code>
     */
    public String getLoginTimeOut() {
        return spec.getDetail(DataSourceSpec.LOGINTIMEOUT);
    }

      /**
     * Sets the delimiter.
     *
     * @param delim <code>String</code>
     * @see <code>getDelimiter</code>
     */
    public void setDelimiter(String delim) {
        spec.setDetail(DataSourceSpec.DELIMITER, delim);
    }

    /**
     * Gets the delimiter.
     *
     * @return delim
     * @see <code>setDelimiter</code>
     */
    public String getDelimiter() {
        return spec.getDetail(DataSourceSpec.DELIMITER);
    }

      /**
     * Sets the driver specific properties.
     *
     * @param driverProps <code>String</code>
     * @see <code>getDriverProperties</code>
     */
    public void setDriverProperties(String driverProps) {
        spec.setDetail(DataSourceSpec.DRIVERPROPERTIES, driverProps);
    }

    /**
     * Gets the driver specific properties.
     *
     * @return driverProps
     * @see <code>setDriverProperties</code>
     */
    public String getDriverProperties() {
        return spec.getDetail(DataSourceSpec.DRIVERPROPERTIES);
    }

    protected ManagedConnection constructManagedConnection(PooledConnection pc,
                                                           Connection sqlCon, PasswordCredential passCred,
                                                           ManagedConnectionFactory mcf) throws ResourceException {
        return new com.sun.gjc.spi.ManagedConnection(pc, sqlCon, passCred, mcf);
    }

    /**
     * Returns the underlying datasource
     * @return DataSource of jdbc vendor
     * @throws ResourceException
     */
    public Object getDataSource() throws ResourceException {
        if (dsObjBuilder == null) {
            dsObjBuilder = new DataSourceObjectBuilder(spec);
        }
        return dsObjBuilder.constructDataSourceObject();
    }

    protected static final int JVM_OPTION_STATEMENT_WRAPPING_ON = 1;
    protected static final int JVM_OPTION_STATEMENT_WRAPPING_OFF = 0;
    protected static final int JVM_OPTION_STATEMENT_WRAPPING_NOT_SET = -1;

    /**
     * This wrapStatement flag is used to enable disable wrapping the
     * statement objects. This can be enabled by passing jvm option,
     * com.sun.appserv.jdbc.wrapstatement = true.
     * This can be disabled by removing this option or by passing non true
     * value.
     * By default this will be disabled
     */
    private static int wrapStatement = JVM_OPTION_STATEMENT_WRAPPING_NOT_SET;

    static {
        wrapStatement = getStatementWrappingJVMOption();
    }

    /**
     * Gets the Statement Wrapping JVM Option (available in 8.2)<br>
     * Which will be deprecated in future versions.
     * @return int representing the JVM Option value of statement wrapping.
     */
    private static int getStatementWrappingJVMOption() {
        int result = JVM_OPTION_STATEMENT_WRAPPING_NOT_SET;
        String str = System.getProperty(
                "com.sun.appserv.jdbc.wrapJdbcObjects");
        if ("true".equalsIgnoreCase(str)) {
            result = JVM_OPTION_STATEMENT_WRAPPING_ON;
        } else if ("false".equalsIgnoreCase(str)) {
            result = JVM_OPTION_STATEMENT_WRAPPING_OFF;
        }
        return result;
    }

    /**
     * 9.1 has attribute "wrap-statements" which will be overriden
     * when JVM option is specified as "true" or "false"
     * JVM Option will be deprecated in future versions.
     */
    protected void computeStatementWrappingStatus() {

        boolean poolProperty = false;
        String statementWrappingString = getStatementWrapping();
        if (statementWrappingString != null)
            poolProperty = Boolean.valueOf(statementWrappingString);

        if (wrapStatement == JVM_OPTION_STATEMENT_WRAPPING_ON ||
                (wrapStatement == JVM_OPTION_STATEMENT_WRAPPING_NOT_SET && poolProperty)) {
            statementWrapping = true;
        } else {
            statementWrapping = false;
        }
    }

    /**
     * Returns whether statement wrapping is enabled or not.<br>
     * @return boolean representing statementwrapping status
     */
    public boolean isStatementWrappingEnabled() {
        return statementWrapping;
    }

    public JdbcObjectsFactory getJdbcObjectsFactory() {
        return jdbcObjectsFactory;
    }
}
