com.sun.gjc.spi.ManagedConnectionImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* 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);
}
}