📄 xaconnectionimpl.java
字号:
/**
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
* statements and notices. Redistributions must also contain a
* copy of this document.
*
* 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote
* products derived from this Software without prior written
* permission of Exoffice Technologies. For written permission,
* please contact info@exolab.org.
*
* 4. Products derived from this Software may not be called "Exolab"
* nor may "Exolab" appear in their names without prior written
* permission of Exoffice Technologies. Exolab is a registered
* trademark of Exoffice Technologies.
*
* 5. Due credit should be given to the Exolab Project
* (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved.
*
* $Id: XAConnectionImpl.java,v 1.2 2002/04/21 03:03:46 mark_matthews Exp $
*/
package com.mysql.jdbc.xa;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Vector;
import javax.sql.XAConnection;
import javax.sql.PooledConnection;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.transaction.RollbackException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import javax.transaction.xa.XAException;
/**
* Implements an X/A connection that can be pooled and managed from
* inside a transaction monitor. This is the XA connection returned
* to the application server from the {@link XADataSourceImpl} and
* will be used to obtain {@link ClientConnection} for the
* application.
* <p>
* If the transaction is managed through the JDBC interface, this
* connection will reference the underlying JDBC connection directly.
* If this resource is enlisted with a global transaction through
* the {@link XAResource} interface, it will reference a transactional
* connection, or {@link TxConnection}. Such a connection may be
* shared by two or more XA connections enlisted with the same
* transaction.
*
*
* @author <a href="arkin@exoffice.com">Assaf Arkin</a>
* @version 1.0
* @see ClientConnection
* @see ConnectionEventListener
* @see TxConnection
*/
public final class XAConnectionImpl
implements XAConnection, XAResource
{
/**
* This is the underlying JDBC connection represented
* by this pooled connection. This variable may initially be null,
* in which case {@link #getUnderlying} will return a new
* connection and set this variable. This variable is mutually
* exclusive with {@link #_txConn} and is always null for
* connections inside a transaction.
*/
Connection _underlying;
/**
* If this connection is part of a global transaction, this
* object identifies the transaction. The transaction's
* underlying JDBC connection is exposed through this object and
* {@link #_underlying} is null. If this connection is closed,
* then the connection has been timedout. Commit/rollback will
* always set this variable to null.
*/
private TxConnection _txConn;
/**
* The client connection last handed to the application. If the
* application calls {@link #getConnection} again, we should hand
* out a new client connection and render the previous one closed.
*/
// No longer in use, see _clientId
//private ClientConnection _clientConn;
/**
* An event listener can be registered and notified when the
* client connection has been closed by the application or a
* fatal error rendered it unuseable.
*/
private ConnectionEventListener _listener;
/**
* The resource manager is used to share connections within the
* same transaction.
*/
private XADataSourceImpl _resManager;
/**
* This is an identifier we hand to the client connection when we
* create it. When the client connection asks for the underlying
* connection, we compare the identifiers. If since that point we
* created a new client connection, we regard an old client
* connection as discarded and do not hand it the underlying
* connection.
* <p>
* Previously, when a new client connection was created, we used
* a reference to the old one to terminate it. This proved to
* not work well, since the client connection could never be
* finalized.
*/
private int _clientId = 1;
/**
* The user name for the underlying connection.
* Can be null.
*/
private String _userName;
/**
* The password for the underlying connection.
* Can be null.
*/
private String _password;
/**
* Construct a new XA/pooled connection with the underlying JDBC
* connection suitable for this driver only. This is a one to one
* mapping between this connection and the underlying connection.
* The underlying connection is only provided for pooled
* connections. XA connections are suspect of being enlisted with
* a global transaction which might already bear an underlying
* connection. If not, one will be created later on.
*/
XAConnectionImpl( XADataSourceImpl resManager,
Connection underlying,
String userName,
String password )
{
_underlying = underlying;
_resManager = resManager;
_userName = userName;
_password = password;
}
public synchronized void close()
throws SQLException
{
// This is our indication that this connection has been
// closed programmatically.
if ( _resManager == null )
throw new SQLException( "This connection has been closed" );
// The client connection is no longer useable.
/* Deprecated: see _clientId
if ( _clientConn != null )
_clientConn.terminate();
*/
_clientId = -1;
// The underlying connection is closed and this connection
// is no longer useable. This method can be called any number
// of times (e.g. we use it in finalizer). We do not handle
// transactions, we just kill the connection.
try {
if ( _underlying != null ) {
_underlying.commit();
_underlying.close();
} else if ( _txConn != null ) {
try {
end( _txConn.xid, TMSUCCESS );
} catch ( XAException except ) { }
}
} finally {
_resManager = null;
_underlying = null;
_txConn = null;
_listener = null;
}
}
public XAResource getXAResource()
{
// The connection acts as it's own resource manager
return this;
}
public synchronized void addConnectionEventListener( ConnectionEventListener listener )
{
if ( listener == null )
throw new NullPointerException( "XAConnection: Argument 'listener' is null" );
if ( _listener != null )
throw new IllegalStateException( "XAConnection: Only one listener supported per connection" );
_listener = listener;
}
public synchronized void removeConnectionEventListener( ConnectionEventListener listener )
{
if ( listener == null )
throw new NullPointerException( "XAConnection: Argument 'listener' is null" );
if ( _listener == null || _listener != listener )
throw new IllegalStateException( "XAConnection: Listener never registered with this pooled connection" );
_listener = null;
}
public synchronized java.sql.Connection getConnection()
throws SQLException
{
// If this pooled connection has been closed, throw an exception.
if ( _resManager == null )
throw new SQLException( "This connection has been closed" );
// If getConnection() was called before and the underlying
// connection was not closed, we take it away from the previous
// recieved as per the PooledConnection design.
/* Deprecated: see _clientId
if ( _clientConn != null )
_clientConn.terminate();
*/
// If we are handling an underlying connection, we commit the
// old transaction and are ready to work for a new one.
// If we are part of a global transaction we hope that end/
// start were called properly, but we're not longer in that
// transaction.
if ( _underlying != null ) {
try {
_underlying.commit();
} catch ( SQLException except ) {
ConnectionEvent event;
if ( _listener != null ) {
event = new ConnectionEvent( this, except );
_listener.connectionErrorOccurred( event );
}
}
}
// Create a new ClientConnection which will be returned to the
// application. The ClientConnection cannot be closed directly
// and cannot manage it's own transactions.
/* Deprecated: see _clientId
_clientConn = new ClientConnection( this );
return _clientConn;
*/
return new ClientConnection( this, ++_clientId );
}
/**
* Called by {@link ClientConnection} to notify that the application
* has attempted to close the connection. After this call, the client
* connection is no longer useable and this pooled connection can be
* reused. The event listener is notified immediately.
*
* @param clientId The {@link ClientConnection} identifier
*/
synchronized void notifyClose( int clientId )
{
ConnectionEvent event;
// ClientConnection has been closed, we dissociated it from
// the underlying connection and notify any listener that this
// pooled connection can be reused.
/* Deprecated: see clientId
_clientConn.terminate();
_clientConn = null;
*/
// We have to expect being called by a ClientConnection that we
// no longer regard as valid. That's acceptable, we just ignore.
if ( clientId != _clientId )
return;
// If we are handling an underlying connection, we commit the
// old transaction and are ready to work for a new one.
// If we are part of a global transaction we hope that end/
// start were called properly.
if ( _underlying != null ) {
try {
_underlying.commit();
} catch ( SQLException except ) {
if ( _listener != null ) {
event = new ConnectionEvent( this, except );
_listener.connectionErrorOccurred( event );
}
return;
}
}
// Notify the listener.
if ( _listener != null ) {
event = new ConnectionEvent( this );
_listener.connectionClosed( event );
}
}
/**
* Called by {@link ClientConnection} to notify that an error
* occured with the underlying connection. If the error is
* critical, the underlying connection is closed and the listener
* is notified.
*
* @param clientId The {@link ClientConnection} identifier
* @param except The exception raised by the underlying connection
*/
synchronized void notifyError( int clientId, SQLException except )
{
ConnectionEvent event;
if ( clientId != _clientId )
return;
// If the connection is not two-phase commit we cannot determine
// whether the error is critical, we just return. If the connection
// is two phase commit, but the error is not critical, we return.
if ( _underlying != null ) {
if ( ! ( _underlying instanceof TwoPhaseConnection ) ||
! ( (TwoPhaseConnection) _underlying ).isCriticalError( except ) )
return;
if ( _txConn.conn == null ||
! ( _txConn.conn instanceof TwoPhaseConnection ) ||
! ( (TwoPhaseConnection) _txConn.conn ).isCriticalError( except ) )
return;
}
// The client connection is no longer useable, the underlying
// connection (if used) is closed, the TxConnection (if used)
// is rolledback and this connection dies (but close() may
// still be called).
++_clientId;
if ( _underlying != null ) {
try {
_underlying.close();
} catch ( SQLException e2 ) {
// Ignore that, we know there's an error.
}
_underlying = null;
} else if ( _txConn != null ) {
try {
end( _txConn.xid, TMFAIL );
} catch ( XAException e2 ) {
// Ignore that, we know there's an error.
}
_txConn = null;
}
// Notify the listener.
if ( _listener != null ) {
event = new ConnectionEvent( this, except );
_listener.connectionErrorOccurred( event );
}
}
protected void finalize()
throws Throwable
{
// We are no longer referenced by anyone (including the
// connection pool). Time to close down.
close();
}
public String toString()
{
if ( _underlying != null )
return "XAConnection: " + _underlying;
else
return "XAConnection: unused";
}
public synchronized void start( Xid xid, int flags )
throws XAException
{
// General checks.
if ( xid == null )
throw new XAException( XAException.XAER_INVAL );
if ( _txConn != null )
throw new XAException( XAException.XAER_OUTSIDE );
synchronized ( _resManager ) {
if ( flags == TMNOFLAGS ) {
// Starting a new transaction. First, make sure it is
// not shared with any other connection (need to join
// for that).
if ( _resManager.getTxConnection( xid ) != null )
throw new XAException( XAException.XAER_DUPID );
// Create a new TxConnection to describe this
// connection in the context of a transaction and
// register it with the resource manager so it can
// be shared.
try {
_txConn = new TxConnection(_userName, _password);
if ( _underlying != null ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -