All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.gjc.spi.ManagedConnectionImpl Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2016 Oracle and/or its affiliates. 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_1_1.html
 * or packager/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 packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [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.
 */
// Portions Copyright 2016-2022 Payara Foundation and/or its affiliates.

package com.sun.gjc.spi;

import com.sun.appserv.connectors.internal.api.ConnectorsUtil;
import com.sun.appserv.connectors.internal.spi.BadConnectionEventListener;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.ResourcePool;
import com.sun.enterprise.config.serverbeans.Resources;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.gjc.common.DataSourceObjectBuilder;
import com.sun.gjc.spi.base.*;
import com.sun.gjc.spi.base.datastructure.Cache;
import com.sun.gjc.spi.base.datastructure.CacheFactory;
import com.sun.gjc.util.SQLTraceDelegator;
import com.sun.gjc.util.StatementLeakDetector;
import com.sun.logging.LogDomains;
import fish.payara.jdbc.RequestTracingListener;
import fish.payara.jdbc.SQLTraceStoreAdapter;
import fish.payara.jdbc.SlowSQLLogger;
import fish.payara.nucleus.requesttracing.RequestTracingService;

import static java.lang.Double.parseDouble;

import java.io.PrintWriter;
import java.sql.CallableStatement;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.resource.NotSupportedException;
import jakarta.resource.ResourceException;
import jakarta.resource.spi.ConnectionEvent;
import jakarta.resource.spi.ConnectionEventListener;
import jakarta.resource.spi.security.PasswordCredential;
import javax.security.auth.Subject;
import javax.sql.PooledConnection;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;

import org.glassfish.api.StartupRunLevel;
import org.glassfish.api.admin.ProcessEnvironment;
import org.glassfish.hk2.runlevel.RunLevelController;
import org.glassfish.internal.api.Globals;
import org.glassfish.jdbc.config.JdbcConnectionPool;
import org.glassfish.resourcebase.resources.api.PoolInfo;

/**
 * ManagedConnection implementation for Generic JDBC Connector.
 *
 * @author Evani Sai Surya Kiran
 * @version 1.0, 02/07/22
 */
public class ManagedConnectionImpl implements jakarta.resource.spi.ManagedConnection,
        jakarta.resource.spi.LazyEnlistableManagedConnection,
        jakarta.resource.spi.DissociatableManagedConnection {

    public static final int ISNOTAPOOLEDCONNECTION = 0;
    public static final int ISPOOLEDCONNECTION = 1;
    public static final int ISXACONNECTION = 2;

    protected boolean isDestroyed = false;
    protected boolean isUsable = true;
    protected boolean initSqlExecuted = false;
    protected int connectionCount = 0;

    public static final String IS_SLOW_SQL_LOGGING_DISABLED = "-1";

    protected int connectionType = ISNOTAPOOLEDCONNECTION;
    protected PooledConnection pc = null;
    protected java.sql.Connection actualConnection = null;
    protected Hashtable connectionHandles;
    protected PrintWriter logWriter;
    protected PasswordCredential passwdCredential;
    private jakarta.resource.spi.ManagedConnectionFactory mcf = null;
    protected XAResource xar = null;

    protected ConnectionHolder myLogicalConnection = null;

    //GJCINT
    protected int lastTransactionIsolationLevel;
    protected boolean isClean = true;

    protected boolean transactionInProgress = false;

    protected ConnectionEventListener listener = null;

    protected ConnectionEvent ce = null;

    private boolean defaultAutoCommitValue = true;
    private boolean lastAutoCommitValue = defaultAutoCommitValue;

    private boolean markedForRemoval = false;

    //private boolean statemntWrapping;
    private int statementTimeout;

    private Cache statementCache = null;
    private int cacheSize;
    private String cacheType;
    private boolean statementCaching;
    private long stmtLeakTimeout;
    private boolean stmtLeakReclaim;
    private boolean statementLeakTracing;
    protected StatementLeakDetector leakDetector;

    private SQLTraceDelegator sqlTraceDelegator;
    
    protected final static Logger _logger;

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

    protected static final StringManager localStrings =
            StringManager.getManager(DataSourceObjectBuilder.class);
    
    private boolean aborted = false;

    private DatabaseMetaData cachedDatabaseMetaData = null;
    private Boolean isClientInfoSupported = null;
    
    private JdbcConnectionPool connectionPool;
    private RequestTracingService requestTracing;
    
    /**
     * Constructor for ManagedConnectionImpl. The pooledConn parameter is expected
     * to be null and sqlConn parameter is the actual connection in case where
     * the actual connection is got from a non pooled datasource object. The
     * pooledConn parameter is expected to be non null and sqlConn parameter
     * is expected to be null in the case where the datasource object is a
     * connection pool datasource or an xa datasource.
     *
     * @param pooledConn PooledConnection object in case the
     *                   physical connection is to be obtained from a pooled
     *                   DataSource; null otherwise
     * @param sqlConn    java.sql.Connection object in case the physical
     *                   connection is to be obtained from a non pooled DataSource;
     *                   null otherwise
     * @param passwdCred object conatining the
     *                   user and password for allocating the connection
     * @throws ResourceException if the ManagedConnectionFactory object
     *                           that created this ManagedConnectionImpl object
     *                           is not the same as returned by PasswordCredential
     *                           object passed
     */
    public ManagedConnectionImpl(PooledConnection pooledConn, java.sql.Connection sqlConn,
                             PasswordCredential passwdCred, 
                             jakarta.resource.spi.ManagedConnectionFactory mcf,
                             PoolInfo poolInfo,
                             int statementCacheSize, String statementCacheType,
                             SQLTraceDelegator delegator,
                             long statementLeakTimeout, boolean statementLeakReclaim)
            throws ResourceException {
        if (pooledConn == null && sqlConn == null) {

            String i18nMsg = localStrings.getString(
                    "jdbc.conn_obj_null");
            throw new ResourceException(i18nMsg);
        }

        if (connectionType == ISNOTAPOOLEDCONNECTION) {
            actualConnection = sqlConn;
        }

        pc = pooledConn;
        connectionHandles = new Hashtable();
        passwdCredential = passwdCred;
        sqlTraceDelegator = delegator;

        this.mcf = mcf;
        if (passwdCredential != null &&
                this.mcf.equals(passwdCredential.getManagedConnectionFactory()) == false) {

            String i18nMsg = localStrings.getString(
                    "jdbc.mc_construct_err");
            throw new ResourceException(i18nMsg);
        }
        logWriter = mcf.getLogWriter();
        ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
        tuneStatementCaching(poolInfo, statementCacheSize, statementCacheType);
        tuneStatementLeakTracing(poolInfo, statementLeakTimeout, statementLeakReclaim);
        
        connectionPool = getJdbcConnectionPool(mcf);
        try {
            RunLevelController runLevelController = Globals.getDefaultHabitat().getService(RunLevelController.class);
            if (runLevelController != null && runLevelController.getCurrentRunLevel() == StartupRunLevel.VAL) {
                requestTracing = Globals.getDefaultHabitat().getService(RequestTracingService.class);
            }
        } catch (NullPointerException ex) {
            _logger.log(Level.INFO, "Error retrieving Request Tracing service "
                    + "during initialisation of ManagedConnectionImpl - NullPointerException");
        }
    }

    public StatementLeakDetector getLeakDetector() {
        return leakDetector;
    }

    private void executeInitSql(final String initSql) {
        if(_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "jdbc.execute_init_sql_start");
        }
        java.sql.PreparedStatement stmt = null;
        
        if (initSql != null && !initSql.equalsIgnoreCase("null") &&
                !initSql.equals("")) {
        try {
                stmt = actualConnection.prepareStatement(initSql);
                if(_logger.isLoggable(Level.FINE)) {
                    _logger.log(Level.FINE, "jdbc.executing_init_sql", initSql);
                }
                stmt.execute();
            } catch (SQLException sqle) {
                _logger.log(Level.WARNING, "jdbc.exc_init_sql_error", initSql);
                initSqlExecuted = false;
            } finally {
                try {
                    if (stmt != null) {
                        stmt.close();
                    }
                } catch (Exception e) {
                    if(_logger.isLoggable(Level.FINE)) {
                        _logger.log(Level.FINE, "jdbc.exc_init_sql_error_stmt_close", e.getMessage());
                    }
                }
            }
            initSqlExecuted = true;
        }
        if(_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "jdbc.execute_init_sql_end");
        }
    }

    private void tuneStatementCaching(PoolInfo poolInfo, int statementCacheSize,
            String statementCacheType) {
        cacheSize = statementCacheSize;
        cacheType = statementCacheType;
        if (cacheSize > 0) {
            try {
                statementCache = CacheFactory.getDataStructure(poolInfo, cacheType, cacheSize);
                statementCaching = true;
            } catch (ResourceException ex) {
                _logger.severe(ex.getMessage());
            }
        }
    }

    private void tuneStatementLeakTracing(PoolInfo poolInfo, long statementLeakTimeout,
            boolean statementLeakReclaim) {
        stmtLeakTimeout = statementLeakTimeout;
        stmtLeakReclaim = statementLeakReclaim;

        if(stmtLeakTimeout > 0) {
            ManagedConnectionFactoryImpl spiMCF = (ManagedConnectionFactoryImpl) mcf;
            statementLeakTracing = true;
            if (leakDetector == null) {
                leakDetector = new StatementLeakDetector(poolInfo, statementLeakTracing,
                        stmtLeakTimeout, stmtLeakReclaim,
                        ((com.sun.gjc.spi.ResourceAdapterImpl) spiMCF.getResourceAdapter()).getTimer());
            }
        }
    }
    /**
     * Adds a connection event listener to the ManagedConnectionImpl instance.
     *
     * @param listener ConnectionEventListener
     * @see #removeConnectionEventListener
     */
    @Override
    public void addConnectionEventListener(ConnectionEventListener listener) {
        this.listener = listener;
    }

    /**
     * Used by the container to change the association of an application-level
     * connection handle with a ManagedConnectionImpl instance.
     *
     * @param connection ConnectionHolder to be associated with
     *                   this ManagedConnectionImpl instance
     * @throws ResourceException if the physical connection is no more
     *                           valid or the connection handle passed is null
     */
    @Override
    public void associateConnection(Object connection) throws ResourceException {
        logFine("In associateConnection");
        checkIfValid();
        if (connection == null) {
            String i18nMsg = localStrings.getString(
                    "jdbc.conn_handle_null");
            throw new ResourceException(i18nMsg);
        }
        ConnectionHolder ch = (ConnectionHolder) connection;

        com.sun.gjc.spi.ManagedConnectionImpl mc = ch.getManagedConnection();
        isClean = false;

        ch.associateConnection(actualConnection, this);
        /**
         * The expectation from the above method is that the connection holder
         * replaces the actual sql connection it holds with the sql connection
         * handle being passed in this method call. Also, it replaces the reference
         * to the ManagedConnectionImpl instance with this ManagedConnectionImpl instance.
         * Any previous statements and result sets also need to be removed.
         */


        ch.setActive(true);
        incrementCount();
        //associate the MC to the supplied logical connection similar to associating the logical connection
        //with this MC and actual-connection.
        myLogicalConnection = ch;
        
        //mc will be null in case we are lazily associating 
        if (mc != null) {
            mc.decrementCount();
        }
    }

    /**
     * Application server calls this method to force any cleanup on the
     * ManagedConnectionImpl instance. This method calls the invalidate
     * method on all ConnectionHandles associated with this ManagedConnectionImpl.
     *
     * @throws ResourceException if the physical connection is no more valid
     */
    @Override
    public void cleanup() throws ResourceException {
        logFine("In cleanup");
        //commenting this as cleanup is called after destroy in case of markConnectionAsBad()
        //checkIfValid();

        /**
         * may need to set the autocommit to true for the non-pooled case.
         */
        //GJCINT
        isClean = true;

        ManagedConnectionFactoryImpl spiMCF = (ManagedConnectionFactoryImpl) mcf;
        resetConnectionProperties(spiMCF);
    }

    /**
     * This method removes all the connection handles from the table
     * of connection handles and invalidates all of them so that any
     * operation on those connection handles throws an exception.
     *
     * @throws ResourceException if there is a problem in retrieving
     *                           the connection handles
     */
    protected void invalidateAllConnectionHandles() throws ResourceException {
        Set handles = connectionHandles.keySet();
        Iterator iter = handles.iterator();
        try {
            while (iter.hasNext()) {
                ConnectionHolder ch = (ConnectionHolder) iter.next();
                ch.invalidate();
            }
        } catch (java.util.NoSuchElementException nsee) {
            throw new ResourceException("Could not find the connection handle: " + nsee.getMessage());
        }
        connectionHandles.clear();
    }

    private void clearStatementCache() {
        if (statementCache != null) {
            if(_logger.isLoggable(Level.FINE)) {
                _logger.fine("Closing statements in statement cache");
            }
            statementCache.flushCache();
            statementCache.clearCache();
        }
    }

    /**
     * Destroys the physical connection to the underlying resource manager.
     *
     * @throws ResourceException if there is an error in closing the physical connection
     */
    @Override
    public void destroy() throws ResourceException {
        logFine("In destroy");
        //GJCINT
        if (isDestroyed) {
            return;
        }
        clearStatementCache();
        //Connection could be closed even before statement is closed. Connection
        //close need not call statement close() method.
        //When application uses a statement, leakDetector could be started.At
        //this point, there is a need to clear the statement leak tasks
        if (leakDetector != null) {
            leakDetector.clearAllStatementLeakTasks();
        }

        try {
            if (connectionType == ISXACONNECTION || connectionType == ISPOOLEDCONNECTION) {
                pc.close();
                pc = null;
                actualConnection = null;
            } else {
                actualConnection.close();
                actualConnection = null;
            }
        } catch (SQLException sqle) {
            isDestroyed = true;
            passwdCredential = null;
            connectionHandles = null;
            String i18nMsg = localStrings.getString(
                    "jdbc.error_in_destroy");
            ResourceException re = new ResourceException(
                    i18nMsg + sqle.getMessage(), sqle);
            throw re;
        }
        isDestroyed = true;
        passwdCredential = null;
        connectionHandles = null;
    }

    /**
     * Creates a new connection handle for the underlying physical
     * connection represented by the ManagedConnectionImpl instance.
     *
     * @param sub       Subject parameter needed for authentication
     * @param cxReqInfo ConnectionRequestInfo carries the user
     *                  and password required for getting this connection.
     * @return Connection    the connection handle Object
     * @throws ResourceException if there is an error in allocating the
     *                           physical connection from the pooled connection
     * @throws jakarta.resource.spi.SecurityException
     *                           if there is a mismatch between the
     *                           password credentials or reauthentication is requested
     */
    @Override
    public Object getConnection(Subject sub, jakarta.resource.spi.ConnectionRequestInfo cxReqInfo)
            throws ResourceException {
        logFine("In getConnection");
        checkIfValid();
        /** Appserver any way doesnt bother about re-authentication today. So commenting this out now.
         com.sun.gjc.spi.ConnectionRequestInfo cxRequestInfo = (com.sun.gjc.spi.ConnectionRequestInfo) cxReqInfo;
         PasswordCredential passwdCred = SecurityUtils.getPasswordCredential(this.mcf, sub, cxRequestInfo);

         if(SecurityUtils.isPasswordCredentialEqual(this.passwdCredential, passwdCred) == false) {
         throw new jakarta.resource.spi.SecurityException("Re-authentication not supported");
         }
         **/

        //GJCINT
        getActualConnection();
        ManagedConnectionFactoryImpl spiMCF = (ManagedConnectionFactoryImpl) mcf;

        String statementTimeoutString = spiMCF.getStatementTimeout();
        if (statementTimeoutString != null) {
            int timeoutValue = Integer.parseInt(statementTimeoutString);
            if (timeoutValue >= 0) {
                statementTimeout = timeoutValue;
            }
        }
        
        /**
         * Register a RequestTracingListener if request tracing is enabled,
         * creating a SQLTraceDelegator if one does not already exist.
         * This check is required here to prevent having to
         * recreate the connection pool to accommodate dynamic request tracing
         * enabling/disabling.
         */
        
        if (sqlTraceDelegator == null) {
            if ((requestTracing != null && requestTracing.isRequestTracingEnabled())
                    || (isSlowQueryLoggingEnabled())) {
                sqlTraceDelegator = new SQLTraceDelegator(spiMCF.getPoolName(),
                        spiMCF.getApplicationName(), spiMCF.getModuleName());
            }
        }

        if (sqlTraceDelegator != null) {
            if (requestTracing != null && requestTracing.isRequestTracingEnabled()) {
                // This method will only register a request tracing listener 
                // if one doesn't already exist
                sqlTraceDelegator.registerSQLTraceListener(new RequestTracingListener());
            } else {
                /**
                 * If request tracing is not enabled, but there is a SQL trace
                 * delegator, deregister the request tracing listener if one is
                 * registered.
                 */
                sqlTraceDelegator.deregisterSQLTraceListener(RequestTracingListener.class);
            }

            if (!isSlowQueryLoggingEnabled()) {
                sqlTraceDelegator.deregisterSQLTraceListener(SlowSQLLogger.class);
                sqlTraceDelegator.deregisterSQLTraceListener(SQLTraceStoreAdapter.class);
            } else {
                sqlTraceDelegator.registerSQLTraceListener(new SlowSQLLogger(getSlowQueryThresholdInMillis(), TimeUnit.MILLISECONDS));
                sqlTraceDelegator.registerSQLTraceListener(new SQLTraceStoreAdapter());
            }
            /**
             * If there are no longer any listeners registered, set the
             * delegator to null to prevent
             * JdbcObjectsFactory().getConnection(...) from using a profiled
             * wrapper unnecessarily.
             */
            if (!sqlTraceDelegator.listenersRegistered()) {
                sqlTraceDelegator = null;
            }
        }

        myLogicalConnection = spiMCF.getJdbcObjectsFactory().getConnection(
                actualConnection, this, cxReqInfo, spiMCF.isStatementWrappingEnabled(),
                sqlTraceDelegator);
        
        //TODO : need to see if this should be executed for every getConnection
        if (!initSqlExecuted) {
            //Check if Initsql is set and execute it
            String initSql = spiMCF.getInitSql();
            executeInitSql(initSql);
        }
        incrementCount();
        isClean = false;

        myLogicalConnection.setActive(true);

        return myLogicalConnection;
    }

    /**
     * Resett connection properties as connections are pooled by application server
* * @param spiMCF * @throws ResourceException */ private void resetConnectionProperties(ManagedConnectionFactoryImpl spiMCF) throws ResourceException { if (isClean) { // If the ManagedConnection is clean, reset the transaction isolation level to what // it was when it was when this ManagedConnection was cleaned up depending on the // ConnectionRequestInfo passed. spiMCF.resetIsolation(this, lastTransactionIsolationLevel); // reset the autocommit value of the connection if application has modified it. resetAutoCommit(); } } /** * To reset AutoCommit of actual connection. * If the last-auto-commit value is different from default-auto-commit value, reset will happen. * If there is a transaction in progress (because of connection sharing), reset will not happen. * * @throws ResourceException */ private void resetAutoCommit() throws ResourceException { if (defaultAutoCommitValue != getLastAutoCommitValue() && !(isTransactionInProgress())) { try { actualConnection.setAutoCommit(defaultAutoCommitValue); } catch (SQLException sqle) { String i18nMsg = localStrings.getString( "jdbc.error_during_setAutoCommit"); throw new ResourceException(i18nMsg + sqle.getMessage(), sqle); } setLastAutoCommitValue(defaultAutoCommitValue); } } /** * Returns an LocalTransactionImpl instance. The LocalTransactionImpl interface * is used by the container to manage local transactions for a RM instance. * * @return LocalTransactionImpl instance * @throws ResourceException if the physical connection is not valid */ @Override public jakarta.resource.spi.LocalTransaction getLocalTransaction() throws ResourceException { logFine("In getLocalTransaction"); checkIfValid(); return new com.sun.gjc.spi.LocalTransactionImpl(this); } /** * Gets the log writer for this ManagedConnectionImpl instance. * * @return PrintWriter instance associated with this * ManagedConnectionImpl instance * @throws ResourceException if the physical connection is not valid * @see setLogWriter */ @Override public PrintWriter getLogWriter() throws ResourceException { logFine("In getLogWriter"); checkIfValid(); return logWriter; } /** * Gets the metadata information for this connection's underlying EIS * resource manager instance. * * @return ManagedConnectionMetaData instance * @throws ResourceException if the physical connection is not valid */ @Override public jakarta.resource.spi.ManagedConnectionMetaData getMetaData() throws ResourceException { logFine("In getMetaData"); checkIfValid(); return new com.sun.gjc.spi.ManagedConnectionMetaDataImpl(this); } /** * Returns an XAResource instance. * * @return XAResource instance * @throws ResourceException if the physical connection is not valid or * there is an error in allocating the * XAResource instance * @throws NotSupportedException if underlying datasource is not an * XADataSource */ @Override public XAResource getXAResource() throws ResourceException { logFine("In getXAResource"); checkIfValid(); if (connectionType == ISXACONNECTION) { try { if (xar == null) { /** * Using the wrapper XAResource. */ xar = new com.sun.gjc.spi.XAResourceImpl(((XAConnection) pc).getXAResource(), this); } return xar; } catch (SQLException sqle) { throw new ResourceException(sqle.getMessage(), sqle); } } else { throw new NotSupportedException("Cannot get an XAResource from a non XA connection"); } } /** * Removes an already registered connection event listener from the * ManagedConnectionImpl instance. * * @param listener ConnectionEventListener to be removed * @see #addConnectionEventListener */ @Override public void removeConnectionEventListener(ConnectionEventListener listener) { } /** * This method is called from XAResource wrapper object * when its XAResource.start() has been called or from * LocalTransactionImpl object when its begin() method is called. */ void transactionStarted() { transactionInProgress = true; } /** * This method is called from XAResource wrapper object * when its XAResource.end() has been called or from * LocalTransactionImpl object when its end() method is called. */ void transactionCompleted() { try { transactionInProgress = false; if (connectionType == ISPOOLEDCONNECTION || connectionType == ISXACONNECTION) { if (connectionCount <= 0) { try { actualConnection.close(); actualConnection = null; } catch (SQLException sqle) { actualConnection = null; } } } } catch (java.lang.NullPointerException e) { if(_logger.isLoggable(Level.FINE)) { _logger.log(Level.FINE, "jdbc.duplicateTxCompleted"); } } if (markedForRemoval) { if (aborted) { com.sun.appserv.connectors.internal.spi.BadConnectionEventListener bcel = (BadConnectionEventListener) listener; bcel.connectionAbortOccurred(ce); _logger.log(Level.INFO, "jdbc.markedForRemoval_conAborted"); markedForRemoval = false; myLogicalConnection.setClosed(true); } else { connectionErrorOccurred(null, null); _logger.log(Level.INFO, "jdbc.markedForRemoval_txCompleted"); markedForRemoval = false; } } isClean = true; } /** * Checks if a this ManagedConnection is involved in a transaction * or not. */ public boolean isTransactionInProgress() { return transactionInProgress; } /** * Sets the log writer for this ManagedConnectionImpl instance. * * @param out PrintWriter to be associated with this * ManagedConnectionImpl instance * @throws ResourceException if the physical connection is not valid * @see getLogWriter */ @Override public void setLogWriter(PrintWriter out) throws ResourceException { checkIfValid(); logWriter = out; } /** * This method determines the type of the connection being held * in this ManagedConnectionImpl. * * @param pooledConn PooledConnection * @return connection type */ protected int getConnectionType(PooledConnection pooledConn) { if (pooledConn == null) { return ISNOTAPOOLEDCONNECTION; } else if (pooledConn instanceof XAConnection) { return ISXACONNECTION; } else { return ISPOOLEDCONNECTION; } } /** * Returns the ManagedConnectionFactory instance that * created this ManagedConnection instance. * * @return ManagedConnectionFactory instance that created this * ManagedConnection instance */ public com.sun.gjc.spi.ManagedConnectionFactoryImpl getManagedConnectionFactory() { return (com.sun.gjc.spi.ManagedConnectionFactoryImpl) mcf; } /** * Returns the actual sql connection for this ManagedConnection. * * @return the physical java.sql.Connection */ //GJCINT java.sql.Connection getActualConnection() throws ResourceException { //GJCINT if (connectionType == ISXACONNECTION || connectionType == ISPOOLEDCONNECTION) { try { if (actualConnection == null) { actualConnection = pc.getConnection(); //re-initialize lastAutoCommitValue such that resetAutoCommit() wont // affect autoCommit of actualConnection setLastAutoCommitValue(defaultAutoCommitValue); } } catch (SQLException sqle) { throw new ResourceException(sqle.getMessage(), sqle); } } return actualConnection; } /** * Returns the PasswordCredential object associated with this ManagedConnection. * * @return PasswordCredential associated with this * ManagedConnection instance */ PasswordCredential getPasswordCredential() { return passwdCredential; } /** * Checks if this ManagedConnection is valid or not and throws an * exception if it is not valid. A ManagedConnection is not valid if * destroy has not been called and no physical connection error has * occurred rendering the physical connection unusable. * * @throws ResourceException if destroy has been called on this * ManagedConnection instance or if a * physical connection error occurred rendering it unusable */ //GJCINT void checkIfValid() throws ResourceException { if (isDestroyed || !isUsable) { String i18nMsg = localStrings.getString( "jdbc.mc_not_usable"); throw new ResourceException(i18nMsg); } } /** * This method is called by the ConnectionHolder when its close method is * called. This ManagedConnection instance invalidates the connection handle * and sends a CONNECTION_CLOSED event to all the registered event listeners. * * @param e Exception that may have occured while closing the connection handle * @param connHolderObject ConnectionHolder that has been closed * @throws SQLException in case closing the sql connection got out of * getConnection on the underlying * PooledConnection throws an exception */ public void connectionClosed(Exception e, ConnectionHolder connHolderObject) throws SQLException { connHolderObject.invalidate(); decrementCount(); ce.setConnectionHandle(connHolderObject); if (markedForRemoval && !transactionInProgress) { com.sun.appserv.connectors.internal.spi.BadConnectionEventListener bcel = (BadConnectionEventListener) listener; bcel.badConnectionClosed(ce); _logger.log(Level.INFO, "jdbc.markedForRemoval_conClosed"); markedForRemoval = false; } else { listener.connectionClosed(ce); } } /** * This method is called by the ConnectionHolder when it detects a connecion * related error. * * @param e Exception that has occurred during an operation on the physical connection * @param connHolderObject ConnectionHolder that detected the physical * connection error */ void connectionErrorOccurred(Exception e, ConnectionHolder connHolderObject) { ConnectionEventListener cel = this.listener; ConnectionEvent ce = null; ce = e == null ? new ConnectionEvent(this, ConnectionEvent.CONNECTION_ERROR_OCCURRED) : new ConnectionEvent(this, ConnectionEvent.CONNECTION_ERROR_OCCURRED, e); if (connHolderObject != null) { ce.setConnectionHandle(connHolderObject); } cel.connectionErrorOccurred(ce); isUsable = false; } /** * This method is called by the XAResource object when its start method * has been invoked. */ void XAStartOccurred() { try { actualConnection.setAutoCommit(false); } catch (Exception e) { _logger.log(Level.WARNING, "XA Start [ setAutoCommit ] failure ", e); connectionErrorOccurred(e, null); } } /** * This method is called by the XAResource object when its end method * has been invoked. */ void XAEndOccurred() { try { actualConnection.setAutoCommit(true); } catch (Exception e) { _logger.log(Level.WARNING, "XA End [ setAutoCommit ] failure ", e); connectionErrorOccurred(e, null); } } /** * This method is called by a Connection Handle to check if it is * the active Connection Handle. If it is not the active Connection * Handle, this method throws an SQLException. Else, it * returns setting the active Connection Handle to the calling * Connection Handle object to this object if the active Connection * Handle is null. * * @param ch ConnectionHolder that requests this * ManagedConnection instance whether * it can be active or not * @throws SQLException in case the physical is not valid or * there is already an active connection handle */ public void checkIfActive(ConnectionHolder ch) throws SQLException { if (isDestroyed || !isUsable) { String i18nMsg = localStrings.getString( "jdbc.conn_not_usable"); throw new SQLException(i18nMsg); } } /** * sets the connection type of this connection. This method is called * by the MCF while creating this ManagedConnection. Saves us a costly * instanceof operation in the getConnectionType */ public void initializeConnectionType(int _connectionType) { connectionType = _connectionType; } public void incrementCount() { connectionCount++; } public void decrementCount() { connectionCount--; } @Override public void dissociateConnections() { if(myLogicalConnection != null){ myLogicalConnection.dissociateConnection(); myLogicalConnection = null; } } public boolean getLastAutoCommitValue() { return lastAutoCommitValue; } /** * To keep track of last auto commit value. * Helps to reset the auto-commit-value while giving new connection-handle. * * @param lastAutoCommitValue */ public void setLastAutoCommitValue(boolean lastAutoCommitValue) { this.lastAutoCommitValue = lastAutoCommitValue; } public void markForRemoval(boolean flag) { markedForRemoval = flag; } public jakarta.resource.spi.ManagedConnectionFactory getMcf() { return mcf; } public int getStatementTimeout() { return statementTimeout; } public void setLastTransactionIsolationLevel(int isolationLevel) { lastTransactionIsolationLevel = isolationLevel; } /** * Returns the cached DatabaseMetaData. * * @return DatabaseMetaData */ public DatabaseMetaData getCachedDatabaseMetaData() throws ResourceException { if(cachedDatabaseMetaData == null){ try { cachedDatabaseMetaData = getActualConnection().getMetaData(); } catch (SQLException sqle) { throw new ResourceException(sqle.getMessage(), sqle); } } return cachedDatabaseMetaData; } public Boolean isClientInfoSupported() { return isClientInfoSupported; } public void setClientInfoSupported(Boolean isClientInfoSupported) { this.isClientInfoSupported = isClientInfoSupported; } private void logFine(String logMessage){ if(_logger.isLoggable(Level.FINE)) { _logger.log(Level.FINE, logMessage); } } public PreparedStatement prepareCachedStatement( ConnectionWrapper conWrapper, String sql, int resultSetType, int resultSetConcurrency) throws SQLException { if (statementCaching) { CacheObjectKey key = new CacheObjectKey(sql, CacheObjectKey.PREPARED_STATEMENT, resultSetType, resultSetConcurrency); //TODO-SC should a null check be done for statementCache? //TODO-SC refactor this method. PreparedStatementWrapper ps = (PreparedStatementWrapper) statementCache.checkAndUpdateCache(key); //TODO-SC-DEFER can the usability (isFree()) check be done by the cache itself and make sure that only a free stmt is returned if (ps != null) { if (isFree(ps)) { //Find if this ps is a valid one. If invalid, remove it //from the cache and prepare a new stmt & add it to cache if(!ps.isValid()) { statementCache.purge(ps); ps = conWrapper.prepareCachedStatement(sql, resultSetType, resultSetConcurrency, true); ps.setBusy(true); statementCache.addToCache(key, ps, false); } else { //Valid ps ps.setBusy(true); } } else { return conWrapper.prepareCachedStatement(sql, resultSetType, resultSetConcurrency, false); } } else { ps = conWrapper.prepareCachedStatement(sql, resultSetType, resultSetConcurrency, true); ps.setBusy(true); statementCache.addToCache(key, ps, false); } return ps; } else return conWrapper.prepareCachedStatement(sql, resultSetType, resultSetConcurrency, false); } public PreparedStatement prepareCachedStatement( ConnectionWrapper conWrapper, String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { if (statementCaching) { CacheObjectKey key = new CacheObjectKey(sql, CacheObjectKey.PREPARED_STATEMENT, resultSetType, resultSetConcurrency, resultSetHoldability); //TODO-SC should a null check be done for statementCache? PreparedStatementWrapper ps = (PreparedStatementWrapper) statementCache.checkAndUpdateCache(key); //TODO-SC-DEFER can the usability (isFree()) check be done by the cache itself and make sure that only a free stmt is returned if (ps != null) { if (isFree(ps)) { //Find if this ps is a valid one. If invalid, remove it //from the cache and prepare a new stmt & add it to cache if(!ps.isValid()) { statementCache.purge(ps); ps = conWrapper.prepareCachedStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability, true); ps.setBusy(true); statementCache.addToCache(key, ps, false); } else { //Valid ps ps.setBusy(true); } } else { return conWrapper.prepareCachedStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability, false); } } else { ps = conWrapper.prepareCachedStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability, true); statementCache.addToCache(key, ps, false); ps.setBusy(true); } return ps; } else return conWrapper.prepareCachedStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability, false); } public PreparedStatement prepareCachedStatement( ConnectionWrapper conWrapper, String sql, String[] columnNames) throws SQLException { if (statementCaching) { CacheObjectKey key = new CacheObjectKey(sql, CacheObjectKey.PREPARED_STATEMENT, columnNames); //TODO-SC should a null check be done for statementCache? PreparedStatementWrapper ps = (PreparedStatementWrapper) statementCache.checkAndUpdateCache(key); //TODO-SC-DEFER can the usability (isFree()) check be done by the cache itself and make sure that only a free stmt is returned if (ps != null) { if (isFree(ps)) { //Find if this ps is a valid one. If invalid, remove it //from the cache and prepare a new stmt & add it to cache if(!ps.isValid()) { statementCache.purge(ps); ps = conWrapper.prepareCachedStatement(sql, columnNames, true); ps.setBusy(true); statementCache.addToCache(key, ps, false); } else { //Valid ps ps.setBusy(true); } } else { return conWrapper.prepareCachedStatement(sql, columnNames, false); } } else { ps = conWrapper.prepareCachedStatement(sql, columnNames, true); statementCache.addToCache(key, ps, false); ps.setBusy(true); } return ps; } else return conWrapper.prepareCachedStatement(sql, columnNames, false); } public PreparedStatement prepareCachedStatement( ConnectionWrapper conWrapper, String sql, int[] columnIndexes) throws SQLException { if (statementCaching) { CacheObjectKey key = new CacheObjectKey(sql, CacheObjectKey.PREPARED_STATEMENT, columnIndexes); //TODO-SC should a null check be done for statementCache? PreparedStatementWrapper ps = (PreparedStatementWrapper) statementCache.checkAndUpdateCache(key); //TODO-SC-DEFER can the usability (isFree()) check be done by the cache itself and make sure that only a free stmt is returned if (ps != null) { if (isFree(ps)) { //Find if this ps is a valid one. If invalid, remove it //from the cache and prepare a new stmt & add it to cache if(!ps.isValid()) { statementCache.purge(ps); ps = conWrapper.prepareCachedStatement(sql, columnIndexes, true); ps.setBusy(true); statementCache.addToCache(key, ps, false); } else { //Valid ps ps.setBusy(true); } } else { return conWrapper.prepareCachedStatement(sql, columnIndexes, false); } } else { ps = conWrapper.prepareCachedStatement(sql, columnIndexes, true); statementCache.addToCache(key, ps, false); ps.setBusy(true); } return ps; } else return conWrapper.prepareCachedStatement(sql, columnIndexes, false); } public PreparedStatement prepareCachedStatement( ConnectionWrapper conWrapper, String sql, int autoGeneratedKeys) throws SQLException { if (statementCaching) { CacheObjectKey key = new CacheObjectKey(sql, CacheObjectKey.PREPARED_STATEMENT, autoGeneratedKeys); //TODO-SC should a null check be done for statementCache? PreparedStatementWrapper ps = (PreparedStatementWrapper) statementCache.checkAndUpdateCache(key); //TODO-SC-DEFER can the usability (isFree()) check be done by the cache itself and make sure that only a free stmt is returned if (ps != null) { if (isFree(ps)) { //Find if this ps is a valid one. If invalid, remove it //from the cache and prepare a new stmt & add it to cache if(!ps.isValid()) { statementCache.purge(ps); ps = conWrapper.prepareCachedStatement(sql, autoGeneratedKeys, true); ps.setBusy(true); statementCache.addToCache(key, ps, false); } else { //Valid ps ps.setBusy(true); } } else { return conWrapper.prepareCachedStatement(sql, autoGeneratedKeys, false); } } else { ps = conWrapper.prepareCachedStatement(sql, autoGeneratedKeys, true); statementCache.addToCache(key, ps, false); ps.setBusy(true); } return ps; } else return conWrapper.prepareCachedStatement(sql, autoGeneratedKeys, false); } public CallableStatement prepareCachedCallableStatement( ConnectionWrapper conWrapper, String sql, int resultSetType, int resultSetConcurrency) throws SQLException { if (statementCaching) { //Adding the sql as well as the Statement type "CS" to the CacheObjectKey object CacheObjectKey key = new CacheObjectKey(sql, CacheObjectKey.CALLABLE_STATEMENT, resultSetType, resultSetConcurrency); CallableStatementWrapper cs = (CallableStatementWrapper) statementCache.checkAndUpdateCache(key); //TODO-SC-DEFER can the usability (isFree()) check be done by the cache //itself and make sure that only a free stmt is returned if (cs != null) { if (isFree(cs)) { //Find if this cs is a valid one. If invalid, remove it //from the cache and prepare a new stmt & add it to cache if(!cs.isValid()) { statementCache.purge(cs); cs = conWrapper.callableCachedStatement(sql, resultSetType, resultSetConcurrency, true); cs.setBusy(true); statementCache.addToCache(key, cs, false); } else { //Valid ps cs.setBusy(true); } } else { return conWrapper.callableCachedStatement(sql, resultSetType, resultSetConcurrency, false); } } else { cs = conWrapper.callableCachedStatement(sql, resultSetType, resultSetConcurrency, true); statementCache.addToCache(key, cs, false); cs.setBusy(true); } return cs; } else return conWrapper.callableCachedStatement(sql, resultSetType, resultSetConcurrency, false); } public CallableStatement prepareCachedCallableStatement( ConnectionWrapper conWrapper, String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { if (statementCaching) { //Adding the sql as well as the Statement type "CS" to the CacheObjectKey object CacheObjectKey key = new CacheObjectKey(sql, CacheObjectKey.CALLABLE_STATEMENT, resultSetType, resultSetConcurrency, resultSetHoldability); CallableStatementWrapper cs = (CallableStatementWrapper) statementCache.checkAndUpdateCache(key); //TODO-SC-DEFER can the usability (isFree()) check be done by the cache //itself and make sure that only a free stmt is returned if (cs != null) { if (isFree(cs)) { //Find if this cs is a valid one. If invalid, remove it //from the cache and prepare a new stmt & add it to cache if(!cs.isValid()) { statementCache.purge(cs); cs = conWrapper.callableCachedStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability, true); cs.setBusy(true); statementCache.addToCache(key, cs, false); } else { //Valid ps cs.setBusy(true); } } else { return conWrapper.callableCachedStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability, false); } } else { cs = conWrapper.callableCachedStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability, true); statementCache.addToCache(key, cs, false); cs.setBusy(true); } return cs; } else return conWrapper.callableCachedStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability, false); } boolean isFree(PreparedStatementWrapper cachedps) { return !cachedps.isBusy(); } public void setAborted(boolean flag) { aborted = flag; } public boolean isAborted() { return aborted; } public void purgeStatementFromCache(PreparedStatement preparedStatement) { //TODO isValid check for preparedStatement? statementCache.purge(preparedStatement); } private static JdbcConnectionPool getJdbcConnectionPool(jakarta.resource.spi.ManagedConnectionFactory mcf) { if(Globals.getDefaultHabitat().getService(ProcessEnvironment.class).getProcessType() != ProcessEnvironment.ProcessType.Server) { // this is only applicatble in the server environment, // otherwise we bave no domain to draw upon return null; } ManagedConnectionFactoryImpl spiMCF = (ManagedConnectionFactoryImpl) mcf; Resources resources = Globals.getDefaultHabitat().getService(Domain.class).getResources(); ResourcePool pool = (ResourcePool) ConnectorsUtil.getResourceByName(resources, ResourcePool.class, spiMCF.getPoolName()); if (pool instanceof JdbcConnectionPool) { return (JdbcConnectionPool) pool; } return null; } private boolean isSlowQueryLoggingEnabled() { return getSlowQueryThresholdInMillis() > 0; } private long getSlowQueryThresholdInMillis() { if (connectionPool == null) { return -1; } String thresholdInSeconds = connectionPool.getSlowQueryThresholdInSeconds(); if (thresholdInSeconds == null || thresholdInSeconds.isEmpty()) { return -1; } return Math.round(parseDouble(thresholdInSeconds) * 1000); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy