blob: f0443d0245c03ba32624d0f82b753cc202d64928 [file] [log] [blame]
/*
* Copyright (c) 2003, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.jdbcra.spi;
import jakarta.resource.spi.*;
import jakarta.resource.*;
import javax.security.auth.Subject;
import java.io.PrintWriter;
import javax.transaction.xa.XAResource;
import java.util.Set;
import java.util.Hashtable;
import java.util.Iterator;
import javax.sql.PooledConnection;
import javax.sql.XAConnection;
import java.sql.Connection;
import java.sql.SQLException;
import jakarta.resource.NotSupportedException;
import jakarta.resource.spi.security.PasswordCredential;
import com.sun.jdbcra.spi.ConnectionRequestInfo;
import com.sun.jdbcra.spi.LocalTransaction;
import com.sun.jdbcra.spi.ManagedConnectionMetaData;
import com.sun.jdbcra.util.SecurityUtils;
import com.sun.jdbcra.spi.ConnectionHolder;
import java.util.logging.Logger;
import java.util.logging.Level;
/**
* <code>ManagedConnection</code> implementation for Generic JDBC Connector.
*
* @version 1.0, 02/07/22
* @author Evani Sai Surya Kiran
*/
public class ManagedConnection implements jakarta.resource.spi.ManagedConnection {
public static final int ISNOTAPOOLEDCONNECTION = 0;
public static final int ISPOOLEDCONNECTION = 1;
public static final int ISXACONNECTION = 2;
private boolean isDestroyed = false;
private boolean isUsable = true;
private int connectionType = ISNOTAPOOLEDCONNECTION;
private PooledConnection pc = null;
private java.sql.Connection actualConnection = null;
private Hashtable connectionHandles;
private PrintWriter logWriter;
private PasswordCredential passwdCredential;
private jakarta.resource.spi.ManagedConnectionFactory mcf = null;
private XAResource xar = null;
public ConnectionHolder activeConnectionHandle;
//GJCINT
private int isolationLevelWhenCleaned;
private boolean isClean = false;
private boolean transactionInProgress = false;
private ConnectionEventListener listener = null;
private ConnectionEvent ce = null;
private static Logger _logger;
static {
_logger = Logger.getAnonymousLogger();
}
/**
* Constructor for <code>ManagedConnection</code>. 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 <code>PooledConnection</code> object in case the
* physical connection is to be obtained from a pooled
* <code>DataSource</code>; null otherwise
* @param sqlConn <code>java.sql.Connection</code> object in case the physical
* connection is to be obtained from a non pooled <code>DataSource</code>;
* null otherwise
* @param passwdCred object conatining the
* user and password for allocating the connection
* @throws ResourceException if the <code>ManagedConnectionFactory</code> object
* that created this <code>ManagedConnection</code> object
* is not the same as returned by <code>PasswordCredential</code>
* object passed
*/
public ManagedConnection(PooledConnection pooledConn, java.sql.Connection sqlConn,
PasswordCredential passwdCred, jakarta.resource.spi.ManagedConnectionFactory mcf) throws ResourceException {
if(pooledConn == null && sqlConn == null) {
throw new ResourceException("Connection object cannot be null");
}
if (connectionType == ISNOTAPOOLEDCONNECTION ) {
actualConnection = sqlConn;
}
pc = pooledConn;
connectionHandles = new Hashtable();
passwdCredential = passwdCred;
this.mcf = mcf;
if(passwdCredential != null &&
this.mcf.equals(passwdCredential.getManagedConnectionFactory()) == false) {
throw new ResourceException("The ManagedConnectionFactory that has created this " +
"ManagedConnection is not the same as the ManagedConnectionFactory returned by" +
" the PasswordCredential for this ManagedConnection");
}
logWriter = mcf.getLogWriter();
activeConnectionHandle = null;
ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
}
/**
* Adds a connection event listener to the ManagedConnection instance.
*
* @param listener <code>ConnectionEventListener</code>
* @see <code>removeConnectionEventListener</code>
*/
public void addConnectionEventListener(ConnectionEventListener listener) {
this.listener = listener;
}
/**
* Used by the container to change the association of an application-level
* connection handle with a <code>ManagedConnection</code> instance.
*
* @param connection <code>ConnectionHolder</code> to be associated with
* this <code>ManagedConnection</code> instance
* @throws ResourceException if the physical connection is no more
* valid or the connection handle passed is null
*/
public void associateConnection(Object connection) throws ResourceException {
if(logWriter != null) {
logWriter.println("In associateConnection");
}
checkIfValid();
if(connection == null) {
throw new ResourceException("Connection handle cannot be null");
}
ConnectionHolder ch = (ConnectionHolder) connection;
com.sun.jdbcra.spi.ManagedConnection mc = (com.sun.jdbcra.spi.ManagedConnection)ch.getManagedConnection();
mc.activeConnectionHandle = null;
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 ManagedConnection instance with this ManagedConnection instance.
* Any previous statements and result sets also need to be removed.
*/
if(activeConnectionHandle != null) {
activeConnectionHandle.setActive(false);
}
ch.setActive(true);
activeConnectionHandle = ch;
}
/**
* Application server calls this method to force any cleanup on the
* <code>ManagedConnection</code> instance. This method calls the invalidate
* method on all ConnectionHandles associated with this <code>ManagedConnection</code>.
*
* @throws ResourceException if the physical connection is no more valid
*/
public void cleanup() throws ResourceException {
if(logWriter != null) {
logWriter.println("In cleanup");
}
checkIfValid();
/**
* may need to set the autocommit to true for the non-pooled case.
*/
//GJCINT
//if (actualConnection != null) {
if (connectionType == ISNOTAPOOLEDCONNECTION ) {
try {
isolationLevelWhenCleaned = actualConnection.getTransactionIsolation();
} catch(SQLException sqle) {
throw new ResourceException("The isolation level for the physical connection "
+ "could not be retrieved");
}
}
isClean = true;
activeConnectionHandle = null;
}
/**
* 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
*/
private 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();
}
/**
* Destroys the physical connection to the underlying resource manager.
*
* @throws ResourceException if there is an error in closing the physical connection
*/
public void destroy() throws ResourceException{
if(logWriter != null) {
logWriter.println("In destroy");
}
//GJCINT
if(isDestroyed == true) {
return;
}
activeConnectionHandle = null;
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;
throw new ResourceException("The following exception has occured during destroy: "
+ sqle.getMessage());
}
isDestroyed = true;
passwdCredential = null;
connectionHandles = null;
}
/**
* Creates a new connection handle for the underlying physical
* connection represented by the <code>ManagedConnection</code> instance.
*
* @param subject <code>Subject</code> parameter needed for authentication
* @param cxReqInfo <code>ConnectionRequestInfo</code> carries the user
* and password required for getting this connection.
* @return Connection the connection handle <code>Object</code>
* @throws ResourceException if there is an error in allocating the
* physical connection from the pooled connection
* @throws SecurityException if there is a mismatch between the
* password credentials or reauthentication is requested
*/
public Object getConnection(Subject sub, jakarta.resource.spi.ConnectionRequestInfo cxReqInfo)
throws ResourceException {
if(logWriter != null) {
logWriter.println("In getConnection");
}
checkIfValid();
com.sun.jdbcra.spi.ConnectionRequestInfo cxRequestInfo = (com.sun.jdbcra.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();
/**
* The following code in the if statement first checks if this ManagedConnection
* is clean or not. If it is, it resets the transaction isolation level to what
* it was when it was when this ManagedConnection was cleaned up depending on the
* ConnectionRequestInfo passed.
*/
if(isClean) {
((com.sun.jdbcra.spi.ManagedConnectionFactory)mcf).resetIsolation(this, isolationLevelWhenCleaned);
}
ConnectionHolder connHolderObject = new ConnectionHolder(actualConnection, this);
isClean=false;
if(activeConnectionHandle != null) {
activeConnectionHandle.setActive(false);
}
connHolderObject.setActive(true);
activeConnectionHandle = connHolderObject;
return connHolderObject;
}
/**
* Returns an <code>LocalTransaction</code> instance. The <code>LocalTransaction</code> interface
* is used by the container to manage local transactions for a RM instance.
*
* @return <code>LocalTransaction</code> instance
* @throws ResourceException if the physical connection is not valid
*/
public jakarta.resource.spi.LocalTransaction getLocalTransaction() throws ResourceException {
if(logWriter != null) {
logWriter.println("In getLocalTransaction");
}
checkIfValid();
return new com.sun.jdbcra.spi.LocalTransaction(this);
}
/**
* Gets the log writer for this <code>ManagedConnection</code> instance.
*
* @return <code>PrintWriter</code> instance associated with this
* <code>ManagedConnection</code> instance
* @throws ResourceException if the physical connection is not valid
* @see <code>setLogWriter</code>
*/
public PrintWriter getLogWriter() throws ResourceException {
if(logWriter != null) {
logWriter.println("In getLogWriter");
}
checkIfValid();
return logWriter;
}
/**
* Gets the metadata information for this connection's underlying EIS
* resource manager instance.
*
* @return <code>ManagedConnectionMetaData</code> instance
* @throws ResourceException if the physical connection is not valid
*/
public jakarta.resource.spi.ManagedConnectionMetaData getMetaData() throws ResourceException {
if(logWriter != null) {
logWriter.println("In getMetaData");
}
checkIfValid();
return new com.sun.jdbcra.spi.ManagedConnectionMetaData(this);
}
/**
* Returns an <code>XAResource</code> instance.
*
* @return <code>XAResource</code> instance
* @throws ResourceException if the physical connection is not valid or
* there is an error in allocating the
* <code>XAResource</code> instance
* @throws NotSupportedException if underlying datasource is not an
* <code>XADataSource</code>
*/
public XAResource getXAResource() throws ResourceException {
if(logWriter != null) {
logWriter.println("In getXAResource");
}
checkIfValid();
if(connectionType == ISXACONNECTION) {
try {
if(xar == null) {
/**
* Using the wrapper XAResource.
*/
xar = new com.sun.jdbcra.spi.XAResourceImpl(((XAConnection)pc).getXAResource(), this);
}
return xar;
} catch(SQLException sqle) {
throw new ResourceException(sqle.getMessage());
}
} else {
throw new NotSupportedException("Cannot get an XAResource from a non XA connection");
}
}
/**
* Removes an already registered connection event listener from the
* <code>ManagedConnection</code> instance.
*
* @param listener <code>ConnectionEventListener</code> to be removed
* @see <code>addConnectionEventListener</code>
*/
public void removeConnectionEventListener(ConnectionEventListener listener) {
listener = null;
}
/**
* This method is called from XAResource wrapper object
* when its XAResource.start() has been called or from
* LocalTransaction 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
* LocalTransaction object when its end() method is called.
*/
void transactionCompleted() {
transactionInProgress = false;
if(connectionType == ISPOOLEDCONNECTION || connectionType == ISXACONNECTION) {
try {
isolationLevelWhenCleaned = actualConnection.getTransactionIsolation();
} catch(SQLException sqle) {
//check what to do in this case!!
_logger.log(Level.WARNING, "jdbc.notgot_tx_isolvl");
}
try {
actualConnection.close();
actualConnection = null;
} catch(SQLException sqle) {
actualConnection = null;
}
}
isClean = true;
activeConnectionHandle = null;
}
/**
* Checks if a this ManagedConnection is involved in a transaction
* or not.
*/
public boolean isTransactionInProgress() {
return transactionInProgress;
}
/**
* Sets the log writer for this <code>ManagedConnection</code> instance.
*
* @param out <code>PrintWriter</code> to be associated with this
* <code>ManagedConnection</code> instance
* @throws ResourceException if the physical connection is not valid
* @see <code>getLogWriter</code>
*/
public void setLogWriter(PrintWriter out) throws ResourceException {
checkIfValid();
logWriter = out;
}
/**
* This method determines the type of the connection being held
* in this <code>ManagedConnection</code>.
*
* @param pooledConn <code>PooledConnection</code>
* @return connection type
*/
private int getConnectionType(PooledConnection pooledConn) {
if(pooledConn == null) {
return ISNOTAPOOLEDCONNECTION;
} else if(pooledConn instanceof XAConnection) {
return ISXACONNECTION;
} else {
return ISPOOLEDCONNECTION;
}
}
/**
* Returns the <code>ManagedConnectionFactory</code> instance that
* created this <code>ManagedConnection</code> instance.
*
* @return <code>ManagedConnectionFactory</code> instance that created this
* <code>ManagedConnection</code> instance
*/
ManagedConnectionFactory getManagedConnectionFactory() {
return (com.sun.jdbcra.spi.ManagedConnectionFactory)mcf;
}
/**
* Returns the actual sql connection for this <code>ManagedConnection</code>.
*
* @return the physical <code>java.sql.Connection</code>
*/
//GJCINT
java.sql.Connection getActualConnection() throws ResourceException {
//GJCINT
if(connectionType == ISXACONNECTION || connectionType == ISPOOLEDCONNECTION) {
try {
if(actualConnection == null) {
actualConnection = pc.getConnection();
}
} catch(SQLException sqle) {
sqle.printStackTrace();
throw new ResourceException(sqle.getMessage());
}
}
return actualConnection;
}
/**
* Returns the <code>PasswordCredential</code> object associated with this <code>ManagedConnection</code>.
*
* @return <code>PasswordCredential</code> associated with this
* <code>ManagedConnection</code> instance
*/
PasswordCredential getPasswordCredential() {
return passwdCredential;
}
/**
* Checks if this <code>ManagedConnection</code> is valid or not and throws an
* exception if it is not valid. A <code>ManagedConnection</code> is not valid if
* destroy has not been called and no physical connection error has
* occurred rendering the physical connection unusable.
*
* @throws ResourceException if <code>destroy</code> has been called on this
* <code>ManagedConnection</code> instance or if a
* physical connection error occurred rendering it unusable
*/
//GJCINT
void checkIfValid() throws ResourceException {
if(isDestroyed == true || isUsable == false) {
throw new ResourceException("This ManagedConnection is not valid as the physical " +
"connection is not usable.");
}
}
/**
* This method is called by the <code>ConnectionHolder</code> when its close method is
* called. This <code>ManagedConnection</code> 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 <code>ConnectionHolder</code> that has been closed
* @throws SQLException in case closing the sql connection got out of
* <code>getConnection</code> on the underlying
* <code>PooledConnection</code> throws an exception
*/
void connectionClosed(Exception e, ConnectionHolder connHolderObject) throws SQLException {
connHolderObject.invalidate();
activeConnectionHandle = null;
ce.setConnectionHandle(connHolderObject);
listener.connectionClosed(ce);
}
/**
* This method is called by the <code>ConnectionHolder</code> when it detects a connecion
* related error.
*
* @param e Exception that has occurred during an operation on the physical connection
* @param connHolderObject <code>ConnectionHolder</code> that detected the physical
* connection error
*/
void connectionErrorOccurred(Exception e,
com.sun.jdbcra.spi.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 <code>XAResource</code> object when its start method
* has been invoked.
*
*/
void XAStartOccurred() {
try {
actualConnection.setAutoCommit(false);
} catch(Exception e) {
e.printStackTrace();
connectionErrorOccurred(e, null);
}
}
/**
* This method is called by the <code>XAResource</code> object when its end method
* has been invoked.
*
*/
void XAEndOccurred() {
try {
actualConnection.setAutoCommit(true);
} catch(Exception e) {
e.printStackTrace();
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 <code>ConnectionHolder</code> that requests this
* <code>ManagedConnection</code> 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
*/
void checkIfActive(ConnectionHolder ch) throws SQLException {
if(isDestroyed == true || isUsable == false) {
throw new SQLException("The physical connection is not usable");
}
if(activeConnectionHandle == null) {
activeConnectionHandle = ch;
ch.setActive(true);
return;
}
if(activeConnectionHandle != ch) {
throw new SQLException("The connection handle cannot be used as another connection is currently active");
}
}
/**
* 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;
}
}