| /* |
| * 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; |
| } |
| |
| } |