From fcd28f4647426168367b2c86d3da9489bca1d6d9 Mon Sep 17 00:00:00 2001
From: Mark Rotteveel
Date: Thu, 26 Mar 2020 10:51:25 +0100
Subject: [PATCH] JDBC-618 Remove JCA support and eliminate connector-api
dependency
See also jdp-2020-03-remove-jca-support
---
.classpath | 1 -
build.xml | 3 +-
build/archive.xml | 43 +-
build/clean.xml | 3 -
build/compile.xml | 8 +-
build/deploy.xml | 14 -
build/dist.xml | 6 +-
build/init.xml | 18 -
build/maven-release/jaybird-relocation.pom | 2 +-
build/maven-release/jaybird-template.pom | 8 +-
build/test.xml | 3 -
build/user.properties | 2 -
build/util.xml | 9 -
devdoc/jdp/jdp-2020-03-remove-jca-support.md | 4 +-
src/documentation/faq.md | 47 +-
src/documentation/release_notes.md | 55 +-
src/lib/connector-api-1.5.jar | Bin 36479 -> 0 bytes
.../org/firebirdsql/ds/DataSourceFactory.java | 89 +-
.../ds/FBAbstractCommonDataSource.java | 9 +-
.../ds/FBConnectionPoolDataSource.java | 48 +-
.../firebirdsql/ds/FBSimpleDataSource.java | 262 ++--
.../org/firebirdsql/ds/FBXADataSource.java | 139 +--
.../org/firebirdsql/gds/impl/GDSType.java | 2 +-
.../gds/ng/listeners/ExceptionListener.java | 2 +-
.../jaybird/xca/FBConnectionRequestInfo.java | 22 +-
.../jaybird/xca/FBLocalTransaction.java | 221 +---
.../jaybird/xca/FBManagedConnection.java | 1050 +++++++----------
.../xca/FBManagedConnectionFactory.java | 521 +++-----
.../xca/FBManagedConnectionMetaData.java | 100 --
.../jaybird/xca/FBResourceException.java | 179 ---
.../xca/FBResourceTransactionException.java | 77 --
.../xca/FBStandAloneConnectionManager.java | 111 +-
.../jaybird/xca/FatalGDSErrorHelper.java | 69 +-
.../jaybird/xca/FirebirdLocalTransaction.java | 50 -
.../jaybird/xca/XcaConnectionEvent.java | 121 ++
.../xca/XcaConnectionEventListener.java | 58 +
.../jaybird/xca/XcaConnectionManager.java | 49 +
.../firebirdsql/jaybird/xca/package-info.java | 6 +-
.../org/firebirdsql/jdbc/FBConnection.java | 77 +-
.../jdbc/FBConnectionProperties.java | 5 +-
.../org/firebirdsql/jdbc/FBDataSource.java | 95 +-
.../firebirdsql/jdbc/FBDatabaseMetaData.java | 1 +
src/main/org/firebirdsql/jdbc/FBDriver.java | 27 +-
.../org/firebirdsql/jdbc/FBSQLException.java | 85 +-
.../org/firebirdsql/jdbc/FBTpbMapper.java | 49 +-
.../firebirdsql/jdbc/FirebirdConnection.java | 156 ++-
.../org/firebirdsql/jdbc/FirebirdDriver.java | 61 +-
.../jdbc/InternalTransactionCoordinator.java | 95 +-
src/resources/META-INF/ra.xml | 97 --
src/stylesheets/junit-frames.xsl | 2 +-
.../firebirdsql/common/FBTestProperties.java | 13 +-
.../firebirdsql/ds/TestDataSourceFactory.java | 57 +-
.../firebirdsql/ds/TestFBXADataSource.java | 5 +-
.../xca/InternalConnectionManager.java | 69 --
.../firebirdsql/jaybird/xca/TestFBBlob.java | 13 +-
.../jaybird/xca/TestFBConnection.java | 36 +-
.../xca/TestFBManagedConnectionFactory.java | 13 +-
.../jaybird/xca/TestFBResultSet.java | 34 +-
.../TestFBStandAloneConnectionManager.java | 10 +-
.../jaybird/xca/TestFBXAResource.java | 68 +-
.../firebirdsql/jaybird/xca/TestXABase.java | 102 +-
.../org/firebirdsql/jdbc/TestFBTpbMapper.java | 20 +-
62 files changed, 1660 insertions(+), 2941 deletions(-)
delete mode 100644 build/clean.xml
delete mode 100644 build/deploy.xml
delete mode 100644 src/lib/connector-api-1.5.jar
delete mode 100644 src/main/org/firebirdsql/jaybird/xca/FBManagedConnectionMetaData.java
delete mode 100644 src/main/org/firebirdsql/jaybird/xca/FBResourceException.java
delete mode 100644 src/main/org/firebirdsql/jaybird/xca/FBResourceTransactionException.java
delete mode 100644 src/main/org/firebirdsql/jaybird/xca/FirebirdLocalTransaction.java
create mode 100644 src/main/org/firebirdsql/jaybird/xca/XcaConnectionEvent.java
create mode 100644 src/main/org/firebirdsql/jaybird/xca/XcaConnectionEventListener.java
create mode 100644 src/main/org/firebirdsql/jaybird/xca/XcaConnectionManager.java
delete mode 100644 src/resources/META-INF/ra.xml
delete mode 100644 src/test/org/firebirdsql/jaybird/xca/InternalConnectionManager.java
diff --git a/.classpath b/.classpath
index d47db07e7a..32a6175a63 100644
--- a/.classpath
+++ b/.classpath
@@ -14,7 +14,6 @@
i`7G;-rD_4S+6{a_V444XB-
zPpY_5x&l$`l%p4SAjWlf9OI4jN7Nd`atw2dQ}5DdK%mJl}92Q!;J$0=(3%d-ex
zaQ4V;^uOP$szYrS1^^83048Sqw?O+{KoB%Gum^DR-wQ^U*hx8n-X(TMs3a(QZf2zM
zqi-SD ra;
z
+ * If you need a standalone connection pool, consider using a connection pool implementation like HikariCP, c3p0 or
+ * DBCP.
+ *
- * The primary use case of this interface is to bridge the gap between the JCA managed connection or connection
+ * The primary use case of this interface is to bridge the gap between the XCA managed connection or connection
* pools that need to detect fatal errors. In the implementation only the methods defined in the various {@code Fb*}
* interfaces in {@code org.firebirdsql.gds.ng} are required to notify the listeners.
*
+ * The FBLocalTransaction is used by the container to manage local
* transactions for a RM instance.
+ *
- * The log writer is a character output stream to which all logging and
- * tracing messages for this ManagedConnection instance will be printed.
- * Application Server manages the association of output stream with the
- * ManagedConnection instance based on the connection pooling requirements.
- *
- * When a ManagedConnection object is initially created, the default log
- * writer associated with this instance is obtained from the
- *
- * The log writer is a character output stream to which all logging and
- * tracing messages for this ManagedConnection instance will be printed.
- *
- * The Log writer associated with a
- * The resource adapter is required to implement the associateConnection
- * method. The method implementation for a ManagedConnection should
- * dissociate the connection handle (passed as a parameter) from its
- * currently associated ManagedConnection and associate the new connection
- * handle with itself.
+ * Application server calls this method to force any cleanup on the managed connection instance.
+ *
+ * The method {@code cleanup} initiates a cleanup of the any client-specific state as maintained by a managed
+ * connection instance. The cleanup should invalidate all connection handles that had been created using this
+ * managed connection instance. Any attempt by an application component to use the connection handle after cleanup
+ * of the underlying managed connection should result in an exception.
+ *
+ * The cleanup of managed connection is always driven by an application server. An application server should not
+ * invoke {@code cleanup} when there is an uncompleted transaction (associated with a managed connection instance)
+ * in progress.
+ *
+ * The invocation of the {@code cleanup} method on an already cleaned-up connection should not throw an exception.
+ *
+ * The cleanup of a managed connection instance resets its client specific state and prepares the connection to be
+ * put back in to a connection pool. The cleanup method should not cause resource adapter to close the physical pipe
+ * and reclaim system resources associated with the physical connection.
+ *
- * The method {@link ManagedConnection#cleanup}initiates a cleanup of the
- * any client-specific state as maintained by a ManagedConnection instance.
- * The cleanup should invalidate all connection handles that had been
- * created using this
- * The cleanup of ManagedConnection is always driven by an application
- * server. An application server should not invoke
- * {@link ManagedConnection#cleanup}when there is an uncompleted
- * transaction (associated with a ManagedConnection instance) in progress.
- *
- * The invocation of {@link ManagedConnection#cleanup}method on an already
- * cleaned-up connection should not throw an exception.
- *
- * The cleanup of
- *
- * The
+ * To manage the size of the connection pool, an application server can explicitly call {@code destroy} to destroy
+ * a physical connection. A resource adapter should destroy all allocated system resources for this managed
+ * connection instance when the method destroy is called.
+ *
- * In both
+ * To support xa semantics, the correct db handle must be located whenever a managed connection is associated with a
+ * xid.
+ *
* It is also used to return a canonical instance to {@link org.firebirdsql.jdbc.FBDriver}.
*
- * This method returns a
- * The
- * Example:
- *
* In case of Firebird, few errors belong to the so-called "fatal errors", after
- * which client application cannot continue its job. For example, when socket
- * connection to the server is broken, any subsequent operation will fail. JCA
+ * which client application cannot continue its job. For example, when socket
+ * connection to the server is broken, any subsequent operation will fail. XCA
* container should remove the connection from the pool in order to allow process
* to recover (when Firebird server is restarted).
- *
+ *
* This list has been kindly reviewed by Ann Harrison, 12/13/2002
+ *
+ * A managed connection notifies its listeners by calling this method when an application component closes a
+ * connection handle. The owner of the managed connection can use this event to put the managed connection instance
+ * back in to the connection pool, or close the physical connection.
+ *
+ * The managed connection instance calls this method to notify its listeners of the occurrence of a physical
+ * connection-related error. The event notification happens just before it throws an exception to the application
+ * component using the connection handle.
+ *
+ * This method indicates that the associated managed connection instance is now invalid and unusable. The owner of
+ * the managed connection handles the connection error event notification by initiating owner-specific cleanup (for
+ * example, removing the managed connection instance from the connection pool) and then calling the destroy method
+ * to destroy the physical connection.
+ *
+ * The returned connection should behave as a new connection, but may be backed by an already established (eg
+ * pooled) managed connection.
+ *
+ * All classes, interfaces and other constructs in this package should be considered internal API of Jaybird, and may
+ * change radically between point releases. Do not use it in your own code.
+ *
+ * This data source is for internal use inside Jaybird. For a simple data source, use
+ * {@link org.firebirdsql.ds.FBSimpleDataSource}, for XA {@link org.firebirdsql.ds.FBXADataSource}.
+ *
+ * If you need a standalone connection pool, consider using a connection pool implementation like HikariCP, c3p0 or
+ * DBCP.
+ *
+ * This method replaces the default TPB mapping with the specified one,
* changes will be effective from the next transaction start.
- *
- * @param isolationLevel isolation level defined in the {@link Connection}
- * interface.
- *
- * @param tpb instance of {@link TransactionParameterBuffer} with parameters
- * to set.
- *
- * @throws SQLException if error occured during this operation.
+ *
- * Method cannot be called when transaction has already started.
- *
- * @param tpb instance of {@link TransactionParameterBuffer} with new
- * transaction parameters.
- *
- * @throws SQLException if method is called within a transaction.
+ * This method does not change the TPB mapping, but replaces the mapping for the current transaction isolation
+ * until {@link Connection#setTransactionIsolation(int)} is called.
+ *
+ * Method cannot be called when transaction has already started.
+ * -ms|Qp&wCT3*j#Q&Ezqp}Hro?mPYU{{CZnLXmW}9srUOyfm8
* *a8
!jC&dP5c<=^)06?V*!U?W$mA15ia
zHklFVB;KuxGgi*5%6xDI5tO&8;u|7o0-8$$*c6?kygmEA{#U*8k96us{w7TEtO5l6
zB-^G=6A
oTs_Zd4iXpo*)#^f{+`R;#huMsqydJ_vX8u@ijvfQ51
z`CUz(vT5~Po!s$6-}hvBL9U>FsnhJlookh`zEcB5FCS@{Jj)s%p!4E&vzxbm;bkjL
zQe~CoGP#-Oa%Z4D-)8e(KIx-|+ys^>RG3*kqnL!mcMV*5)G?{javax.resource.spi.ConnectionEventListener
callback for
- * when a ManagedConnection
is closed.
- *
- * @param ce
- * contains information about the connection that has be
- * closed
- */
- public void connectionClosed(ConnectionEvent ce) {
- PrintWriter externalLog = ((FBManagedConnection) ce.getSource()).getLogWriter();
+ @Override
+ public void connectionClosed(XcaConnectionEvent ce) {
try {
- ((FBManagedConnection) ce.getSource()).destroy(ce);
- } catch (ResourceException e) {
- if (externalLog != null)
- externalLog.println("Exception closing unmanaged connection: " + e);
+ ce.getSource().destroy(ce);
+ } catch (SQLException e) {
+ LOG.warn("Exception closing unmanaged connection", e);
}
}
- /**
- * javax.resource.spi.ConnectionEventListener
callback for
- * when a Local Transaction was rolled back within the context of a
- * ManagedConnection
.
- *
- * @param ce
- * contains information about the connection
- */
- public void connectionErrorOccurred(ConnectionEvent ce) {
- PrintWriter externalLog = ((FBManagedConnection) ce.getSource()).getLogWriter();
+ @Override
+ public void connectionErrorOccurred(XcaConnectionEvent ce) {
try {
- ((FBManagedConnection) ce.getSource()).destroy(ce);
- } catch (ResourceException e) {
- if (externalLog != null)
- externalLog.println("Exception closing unmanaged connection: " + e);
+ ce.getSource().destroy(ce);
+ } catch (SQLException e) {
+ LOG.warn("Exception closing unmanaged connection", e);
}
}
-
- // We are only supposed to be notified of local transactions that a
- // Connection started.
- // Not much we can do with this info...
-
- /**
- * Ignored event callback
- */
- public void localTransactionStarted(ConnectionEvent event) {
- }
-
- /**
- * Ignored event callback
- */
- public void localTransactionCommitted(ConnectionEvent event) {
- }
-
- /**
- * Ignored event callback
- */
- public void localTransactionRolledback(ConnectionEvent event) {
- }
}
public Reference getReference() throws NamingException {
- Reference ref = new Reference(getClass().getName(), DataSourceFactory.class.getName(), null);
+ Reference ref = new Reference(FBXADataSource.class.getName(), DataSourceFactory.class.getName(), null);
FBAbstractCommonDataSource.updateReference(ref, this);
diff --git a/src/main/org/firebirdsql/gds/impl/GDSType.java b/src/main/org/firebirdsql/gds/impl/GDSType.java
index ae27f8e55c..5d73606377 100644
--- a/src/main/org/firebirdsql/gds/impl/GDSType.java
+++ b/src/main/org/firebirdsql/gds/impl/GDSType.java
@@ -95,7 +95,7 @@ private GDSType(String s) {
name = s;
}
- public Object readResolve() {
+ protected Object readResolve() {
return registerType(name);
}
diff --git a/src/main/org/firebirdsql/gds/ng/listeners/ExceptionListener.java b/src/main/org/firebirdsql/gds/ng/listeners/ExceptionListener.java
index 5a0bb443cb..a8f89a1c3c 100644
--- a/src/main/org/firebirdsql/gds/ng/listeners/ExceptionListener.java
+++ b/src/main/org/firebirdsql/gds/ng/listeners/ExceptionListener.java
@@ -29,7 +29,7 @@
/**
* Listener for notifications of SQL Exceptions that occurred in the object listened on.
* FBConnectionRequestInfo
holds a clumplet that is
+ * The class {@code FBConnectionRequestInfo} holds a clumplet that is
* used to store and transfer connection-specific information such as user,
- * password, and other dpb information..
+ * password, and other dpb information.
*
* @author David Jencks
* @author Roman Rokytskyy
- * @version 2.0
*/
-public class FBConnectionRequestInfo implements DatabaseParameterBufferExtension, ConnectionRequestInfo,
- ConnectionSpec, Serializable {
+public class FBConnectionRequestInfo implements DatabaseParameterBufferExtension, Serializable {
+ private static final long serialVersionUID = 1L;
+
private final DatabaseParameterBuffer dpb;
public FBConnectionRequestInfo(DatabaseParameterBuffer dpb) {
@@ -164,16 +162,19 @@ public int size() {
public void setUserName(String userName) {
removeArgument(DatabaseParameterBufferExtension.USER_NAME);
- if (userName != null)
+ if (userName != null) {
addArgument(DatabaseParameterBufferExtension.USER_NAME, userName);
+ }
}
public void setPassword(String password) {
removeArgument(DatabaseParameterBufferExtension.PASSWORD);
- if (password != null)
+ if (password != null) {
addArgument(DatabaseParameterBufferExtension.PASSWORD, password);
+ }
}
+ @Override
public boolean equals(Object obj) {
if (obj == this)
return true;
@@ -184,6 +185,7 @@ public boolean equals(Object obj) {
return this.dpb.equals(((FBConnectionRequestInfo) obj).dpb);
}
+ @Override
public int hashCode() {
return dpb.hashCode();
}
diff --git a/src/main/org/firebirdsql/jaybird/xca/FBLocalTransaction.java b/src/main/org/firebirdsql/jaybird/xca/FBLocalTransaction.java
index e685e702c7..13c11f81ae 100644
--- a/src/main/org/firebirdsql/jaybird/xca/FBLocalTransaction.java
+++ b/src/main/org/firebirdsql/jaybird/xca/FBLocalTransaction.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -18,173 +18,77 @@
*/
package org.firebirdsql.jaybird.xca;
-import org.firebirdsql.jdbc.FBConnection;
import org.firebirdsql.jdbc.SQLStateConstants;
-import javax.resource.ResourceException;
-import javax.resource.spi.ConnectionEvent;
-import javax.resource.spi.EISSystemException;
-import javax.resource.spi.LocalTransactionException;
-import javax.resource.spi.ResourceAdapterInternalException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.sql.SQLException;
/**
- * The class {@code FBLocalTransaction} implements LocalTransaction both
- * in the cci and spi meanings. A flag is used to distinguish the current
- * functionality. This class works by delegating the operations to the internal
- * implementations of the XAResource functionality in FBManagedConnection.
+ * The class {@code FBLocalTransaction} represent a local, not distributed, transaction. A flag is used to
+ * distinguish the current functionality. This class works by delegating the operations to the internal implementation
+ * of the XAResource functionality in FBManagedConnection.
*
* @author David Jencks
- * @version 1.0
*/
-public class FBLocalTransaction implements FirebirdLocalTransaction,
- javax.resource.cci.LocalTransaction {
+public final class FBLocalTransaction {
- protected final FBManagedConnection mc;
+ private final FBManagedConnection mc;
+ private Xid xid = null;
- protected Xid xid = null;
-
- // used to determine if local transaction events notify
- // ConnectionEventListeners see jca spec section 6.8. Basically
- // not null means this is cci LocalTransaction, null means
- // spi.LocalTransaction.
- protected final ConnectionEvent beginEvent;
-
- protected final ConnectionEvent commitEvent;
-
- protected final ConnectionEvent rollbackEvent;
-
- // should be package!!! perhaps reorganize and eliminate jdbc!!!
- public FBLocalTransaction(FBManagedConnection mc, FBConnection c) {
+ FBLocalTransaction(FBManagedConnection mc) {
this.mc = mc;
- if (c == null) {
- beginEvent = null;
- commitEvent = null;
- rollbackEvent = null;
- } else {
- beginEvent = new ConnectionEvent(mc,
- ConnectionEvent.LOCAL_TRANSACTION_STARTED, null);
- beginEvent.setConnectionHandle(c);
-
- commitEvent = new ConnectionEvent(mc,
- ConnectionEvent.LOCAL_TRANSACTION_COMMITTED, null);
- commitEvent.setConnectionHandle(c);
-
- rollbackEvent = new ConnectionEvent(mc,
- ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK, null);
- rollbackEvent.setConnectionHandle(c);
- }
}
/**
- * Get the associated Xid.
+ * Check if managed connection is currently participating in transaction.
*
- * @return instance of {@link Xid} representing a transaction ID that is managed by this local transaction.
- */
- public Xid getXid() {
- return xid;
- }
-
- /**
- * Check whether a started transaction is associated with the current
- * database connection.
+ * @return {@code true} if managed connection is participating in transaction.
+ * @throws SQLException
+ * if operation cannot be completed.
*/
- public boolean inTransaction() throws ResourceException {
- try {
- return mc.getGDSHelper().inTransaction();
- } catch (SQLException ex) {
- throw new FBResourceException(ex);
- }
+ public boolean inTransaction() throws SQLException {
+ return mc.getGDSHelper().inTransaction();
}
/**
* Begin a local transaction.
*
- * @throws ResourceException
- * generic exception if operation fails
- * @throws LocalTransactionException
- * error condition related to local transaction management
- * @throws ResourceAdapterInternalException
- * error condition internal to resource adapter
- * @throws EISSystemException
- * EIS instance specific error condition
- */
- public void begin() throws ResourceException {
- internalBegin();
- }
-
- /**
- * Perform the internal operations to begin a local transaction.
- *
- * @throws ResourceException
+ * @throws SQLException
* generic exception if operation fails
- * @throws LocalTransactionException
- * error condition related to local transaction management
- * @throws ResourceAdapterInternalException
- * error condition internal to resource adapter
- * @throws EISSystemException
- * EIS instance specific error condition
*/
- public void internalBegin() throws ResourceException {
- if (xid != null) {
-
- // throw exception only if xid is known to the managed connection
- if (mc.isXidActive(xid))
- throw new FBResourceTransactionException(
- "Local transaction active: can't begin another",
- SQLStateConstants.SQL_STATE_TRANSACTION_ACTIVE);
+ public void begin() throws SQLException {
+ // throw exception only if xid is known to the managed connection
+ if (xid != null && mc.isXidActive(xid)) {
+ // TODO More specific exception, Jaybird error code
+ throw new SQLException("Local transaction active: can't begin another",
+ SQLStateConstants.SQL_STATE_TRANSACTION_ACTIVE);
}
xid = new FBLocalXid();
try {
mc.internalStart(xid, XAResource.TMNOFLAGS);
- } catch (XAException | SQLException ex) {
+ } catch (XAException ex) {
xid = null;
- throw new FBResourceException(ex);
+ if (ex.getCause() instanceof SQLException) {
+ throw (SQLException) ex.getCause();
+ }
+ // TODO More specific exception, Jaybird error code (or is this flow unlikely to hit?)
+ throw new SQLException(ex.getMessage(), ex);
}
-
- if (beginEvent != null)
- mc.notify(FBManagedConnection.localTransactionStartedNotifier, beginEvent);
}
/**
* Commit a local transaction.
*
- * @throws ResourceException
- * generic exception if operation fails
- * @throws LocalTransactionException
- * error condition related to local transaction management
- * @throws ResourceAdapterInternalException
- * error condition internal to resource adapter
- * @throws EISSystemException
- * EIS instance specific error condition
- */
- public void commit() throws ResourceException {
- internalCommit();
- }
-
- /**
- * Perform the internal processing to commit a local transaction.
- *
- * @throws ResourceException
+ * @throws SQLException
* generic exception if operation fails
- * @throws LocalTransactionException
- * error condition related to local transaction management
- * @throws ResourceAdapterInternalException
- * error condition internal to resource adapter
- * @throws EISSystemException
- * EIS instance specific error condition
*/
- public void internalCommit() throws ResourceException {
-
- // if there is no xid assigned, but we are still here,
- // that means that automatic commit was called in managed
- // scenario when managed connection was enlisted in global
- // transaction
+ public void commit() throws SQLException {
+ // if there is no xid assigned, but we are still here, that means that automatic commit was called in managed
+ // scenario when managed connection was enlisted in global transaction
if (xid == null) return;
synchronized (mc.getSynchronizationObject()) {
@@ -192,80 +96,55 @@ public void internalCommit() throws ResourceException {
mc.internalEnd(xid, XAResource.TMSUCCESS);
mc.internalCommit(xid, true);
} catch (XAException ex) {
- throw new FBResourceTransactionException(ex.getMessage(), ex);
- } catch (SQLException ex) {
- throw new FBResourceException(ex);
+ if (ex.getCause() instanceof SQLException) {
+ throw (SQLException) ex.getCause();
+ }
+ // TODO More specific exception, Jaybird error code (or is this flow unlikely to hit?)
+ throw new SQLException(ex.getMessage(), ex);
} finally {
xid = null;
}
- if (commitEvent != null) {
- mc.notify(FBManagedConnection.localTransactionCommittedNotifier, commitEvent);
- }
}
}
/**
* Rollback a local transaction.
*
- * @throws ResourceException
+ * @throws SQLException
* generic exception if operation fails
- * @throws LocalTransactionException
- * error condition related to local transaction management
- * @throws ResourceAdapterInternalException
- * error condition internal to resource adapter
- * @throws EISSystemException
- * EIS instance specific error condition
*/
- public void rollback() throws ResourceException {
- internalRollback();
- }
-
- /**
- * Perform the internal processing to rollback a local transaction.
- *
- * @throws ResourceException
- * generic exception if operation fails
- * @throws LocalTransactionException
- * error condition related to local transaction management
- * @throws ResourceAdapterInternalException
- * error condition internal to resource adapter
- * @throws EISSystemException
- * EIS instance specific error condition
- */
- public void internalRollback() throws ResourceException {
-
- // if there is no xid assigned, but we are still here,
- // that means that automatic commit was called in managed
- // scenario when managed connection was enlisted in global
- // transaction
+ public void rollback() throws SQLException {
+ // if there is no xid assigned, but we are still here, that means that automatic commit was called in managed
+ // scenario when managed connection was enlisted in global transaction
if (xid == null) return;
synchronized (mc.getSynchronizationObject()) {
try {
mc.internalEnd(xid, XAResource.TMSUCCESS); // ??? on flags
- // --FBManagedConnection is its own XAResource
+ // --FBManagedConnection is its own XAResource
mc.internalRollback(xid);
- } catch (XAException | SQLException ex) {
- throw new FBResourceTransactionException(ex.getMessage(), ex);
+ } catch (XAException ex) {
+ if (ex.getCause() instanceof SQLException) {
+ throw (SQLException) ex.getCause();
+ }
+ // TODO More specific exception, Jaybird error code (or is this flow unlikely to hit?)
+ throw new SQLException(ex.getMessage(), ex);
} finally {
xid = null;
}
- if (rollbackEvent != null) {
- mc.notify(FBManagedConnection.localTransactionRolledbackNotifier, rollbackEvent);
- }
}
}
// This is an intentionally non-implemented xid, so if prepare is called
// with it, it won't work.
// Only object identity works for equals!
- public static class FBLocalXid implements Xid {
+ private static final class FBLocalXid implements Xid {
private static final int formatId = 0x0102;// ????????????
- private String strValue;
+ private final String strValue;
- public FBLocalXid() {
+ private FBLocalXid() {
strValue = "Xid[" + hashCode() + "]";
}
diff --git a/src/main/org/firebirdsql/jaybird/xca/FBManagedConnection.java b/src/main/org/firebirdsql/jaybird/xca/FBManagedConnection.java
index 0f88f18781..1398bed4e4 100644
--- a/src/main/org/firebirdsql/jaybird/xca/FBManagedConnection.java
+++ b/src/main/org/firebirdsql/jaybird/xca/FBManagedConnection.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -18,7 +18,10 @@
*/
package org.firebirdsql.jaybird.xca;
-import org.firebirdsql.gds.*;
+import org.firebirdsql.gds.DatabaseParameterBuffer;
+import org.firebirdsql.gds.ISCConstants;
+import org.firebirdsql.gds.JaybirdSystemProperties;
+import org.firebirdsql.gds.TransactionParameterBuffer;
import org.firebirdsql.gds.impl.DatabaseParameterBufferExtension;
import org.firebirdsql.gds.impl.DbAttachInfo;
import org.firebirdsql.gds.impl.GDSHelper;
@@ -29,25 +32,17 @@
import org.firebirdsql.gds.ng.listeners.DefaultDatabaseListener;
import org.firebirdsql.gds.ng.listeners.DefaultStatementListener;
import org.firebirdsql.gds.ng.listeners.ExceptionListener;
-import org.firebirdsql.jdbc.FBConnection;
-import org.firebirdsql.jdbc.SQLStateConstants;
-import org.firebirdsql.jdbc.Synchronizable;
+import org.firebirdsql.jdbc.*;
import org.firebirdsql.jdbc.field.FBField;
import org.firebirdsql.jdbc.field.FieldDataProvider;
import org.firebirdsql.logging.Logger;
import org.firebirdsql.logging.LoggerFactory;
-import org.firebirdsql.util.SQLExceptionChainBuilder;
-import javax.resource.ResourceException;
-import javax.resource.spi.*;
-import javax.resource.spi.security.PasswordCredential;
-import javax.security.auth.Subject;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.io.PrintWriter;
import java.net.SocketTimeoutException;
import java.sql.DriverManager;
import java.sql.SQLException;
@@ -56,16 +51,16 @@
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
- * The class FBManagedConnection
implements both the
- * ManagedConnection and XAResource interfaces.
+ * A physical connection handle to a Firebird database, providing a {@code XAResource}.
*
* @author David Jencks
* @author Mark Rotteveel
* @version 1.0
*/
-public class FBManagedConnection implements ManagedConnection, XAResource, ExceptionListener, Synchronizable {
+public class FBManagedConnection implements ExceptionListener, Synchronizable {
public static final String WARNING_NO_CHARSET = "WARNING: No connection character set specified (property lc_ctype, encoding, charSet or localEncoding), defaulting to character set ";
public static final String ERROR_NO_CHARSET = "Connection rejected: No connection character set specified (property lc_ctype, encoding, charSet or localEncoding). "
@@ -75,10 +70,14 @@ public class FBManagedConnection implements ManagedConnection, XAResource, Excep
private final FBManagedConnectionFactory mcf;
- private final Listtrue
if connection sharing is enabled.
- */
- public boolean isConnectionSharing() {
- return connectionSharing;
- }
-
- /**
- * Enable or disable connection sharing. See {@link #isConnectionSharing()}
- * method for details.
- *
- * @param connectionSharing true
if connection sharing must be
- * enabled.
- * @throws ResourceException If connection sharing state cannot be changed
- */
- public void setConnectionSharing(boolean connectionSharing) throws ResourceException {
- if (!connectionHandles.isEmpty())
- throw new javax.resource.spi.IllegalStateException(
- "Cannot change connection sharing with active connection handles.");
-
- this.connectionSharing = connectionSharing;
- }
- /**
- * Returns a javax.resource.spi.LocalTransaction
instance.
- * The LocalTransaction interface is used by the container to manage local
+ * Returns a {@code FBLocalTransaction} instance.
+ * ManagedConnectionFactory
. An application server can set a
- * log writer specific to this ManagedConnection to log/trace this instance
- * using setLogWriter method.
- *
- * @param out
- * Character Output stream to be associated
- * @throws ResourceException
- * generic exception if operation fails
- * @throws ResourceAdapterInternalException
- * resource adapter related error condition
- */
- public void setLogWriter(PrintWriter out) {
- // ignore, we are using alternative logging
- }
-
- /**
- * Gets the log writer for this ManagedConnection instance.
- * ConnectionManager
manages the association of output stream
- * with the ManagedConnection
instance based on the
- * connection pooling requirements.
- * ManagedConnection
- * instance can be one set as default from the ManagedConnectionFactory
- * (that created this connection) or one set specifically for this instance
- * by the application server.
- *
- * @return Character ourput stream associated with this
- * ManagedConnection
- * @throws ResourceException
- * generic exception if operation fails
- */
- public PrintWriter getLogWriter() {
- return null;// we are using alternative logging
- }
-
- /**
- * Add an ConnectionEventListener
listener. The listener will
- * be notified when a ConnectionEvent
occurs.
+ * Add an {@code XcaConnectionEventListener} listener. The listener will be notified when a
+ * {@code XcaConnectionEvent} occurs.
*
* @param listener
- * The ConnectionEventListener
to be added
+ * The {@code XcaConnectionEventListener} to be added
*/
- public void addConnectionEventListener(ConnectionEventListener listener) {
+ public void addConnectionEventListener(XcaConnectionEventListener listener) {
connectionEventListeners.add(listener);
}
/**
- * Remove a ConnectionEventListner
from the listing of
- * listeners that will be notified for a ConnectionEvent
.
+ * Remove a {@code XcaConnectionEventListener} from the listing of listeners that will be notified for a
+ * {@code XcaConnectionEvent}.
*
* @param listener
- * The ConnectionEventListener
to be removed
+ * The {@code FirebirdConnectionEventListener} to be removed
*/
- public void removeConnectionEventListener(ConnectionEventListener listener) {
+ public void removeConnectionEventListener(XcaConnectionEventListener listener) {
connectionEventListeners.remove(listener);
}
/**
- * Used by the container to change the association of an application-level
- * connection handle with a ManagedConneciton instance. The container should
- * find the right ManagedConnection instance and call the
- * associateConnection method.
- * ManagedConnection
instance.
- * ManagedConnection
instance. Any attempt
- * by an application component to use the connection handle after cleanup of
- * the underlying ManagedConnection
should result in an
- * exception.
- * ManagedConnection
instance resets its
- * client specific state and prepares the connection to be put back in to a
- * connection pool. The cleanup method should not cause resource adapter to
- * close the physical pipe and reclaim system resources associated with the
- * physical connection.
- *
- * @throws ResourceException
- * generic exception if operation fails
- * @throws ResourceAdapterInternalException
- * resource adapter internal error condition
- * @throws javax.resource.spi.IllegalStateException
- * Illegal state for calling connection cleanup. Example - if a
- * local transaction is in progress that doesn't allow
- * connection cleanup
- */
- public void cleanup() throws ResourceException {
- disassociateConnections();
-
- try {
getGDSHelper().setCurrentTransaction(null);
- } catch (SQLException e) {
- throw new FBResourceException(e);
- }
- // reset the TPB from the previous transaction.
- this.tpb = mcf.getDefaultTpb();
- this.transactionIsolation = mcf.getDefaultTransactionIsolation();
+ // reset the TPB from the previous transaction.
+ tpb = mcf.getDefaultTpb();
+ transactionIsolation = mcf.getDefaultTransactionIsolation();
+ }
}
/**
* Disassociate connections from current managed connection.
*/
- private void disassociateConnections() throws ResourceException {
- SQLExceptionChainBuilderManagedConnection
instance. This
- * connection handle is used by the application code to refer to the
- * underlying physical connection. A connection handle is tied to its
- * ManagedConnection
instance in a resource adapter
- * implementation specific way.
- * ManagedConnection
uses the Subject and additional
- * ConnectionRequestInfo
(which is specific to resource
- * adapter and opaque to application server) to set the state of the
- * physical connection.
+ * Creates a new connection handle for the underlying physical connection represented by the managed connection
+ * instance. This connection handle is used by the application code to refer to the underlying physical connection.
*
- * @param subject
- * security context as JAAS subject
- * @param cri
- * ConnectionRequestInfo instance
- * @return generic Object
instance representing the
- * connection handle. For CCI, the connection handle created by a
- * ManagedConnection
instance is of the type
- * javax.resource.cci.Connection
.
- * @throws ResourceException
- * generic exception if operation fails
- * @throws ResourceAdapterInternalException
- * resource adapter internal error condition
- * @throws javax.resource.spi.SecurityException
- * security related error condition
- * @throws CommException
- * failed communication with EIS instance
- * @throws EISSystemException
- * internal error condition in EIS instance - used if EIS
- * instance is involved in setting state of
- * ManagedConnection
+ * @return instance representing the connection handle
+ * @throws SQLException
+ * generic exception if operation fails
*/
- public Object getConnection(Subject subject, ConnectionRequestInfo cri)
- throws ResourceException {
-
- if (!matches(subject, cri))
- throw new FBResourceException("Incompatible subject or ConnectionRequestInfo in getConnection!");
-
- if (!connectionSharing)
- disassociateConnections();
+ public FBConnection getConnection() throws SQLException {
+ disassociateConnections();
FBConnection c = mcf.newConnection(this);
- try {
- if (unnotifiedWarnings != null) {
- c.addWarning(unnotifiedWarnings);
- unnotifiedWarnings = null;
+ c.setManagedEnvironment(isManagedEnvironment());
+ FBConnection previous = connectionHandleUpdater.getAndSet(this, c);
+ if (previous != null) {
+ previous.setManagedConnection(null);
+ if (log.isDebugEnabled()) {
+ // This would indicate a concurrent getConnection call on this managed connection
+ log.debug("A connection was already associated with the managed connection",
+ new RuntimeException("debug call trace"));
+ }
+ try {
+ previous.setManagedConnection(null);
+ previous.close();
+ } catch (SQLException e) {
+ log.debug("Error forcing previous connection to close", e);
}
- c.setManagedEnvironment(isManagedEnvironment());
- connectionHandles.add(c);
- return c;
- } catch(SQLException ex) {
- throw new FBResourceException(ex);
}
+ final SQLWarning warnings = unnotifiedWarningsUpdater.getAndSet(this, null);
+ if (warnings != null) {
+ c.addWarning(warnings);
+ }
+ return c;
}
/**
- * Destroys the physical connection to the underlying resource manager. To
- * manage the size of the connection pool, an application server can
- * explictly call {@link ManagedConnection#destroy}to destroy a physical
- * connection. A resource adapter should destroy all allocated system
- * resources for this ManagedConnection
instance when the
- * method destroy is called.
+ * Destroys the physical connection to the underlying resource manager.
+ * javax.sql.XAConnection
and
- * javax.resource.spi.MangagedConnection
.
+ * Returns an {@code javax.transaction.xa.XAresource} instance. An application server enlists this XAResource
+ * instance with the Transaction Manager if the FBManagedConnection instance is being used in a Java EE transaction
+ * that is coordinated by the Transaction Manager.
*
- * @return the XAResource
+ * @return XAResource instance
*/
public XAResource getXAResource() {
log.debug("XAResource requested from FBManagedConnection");
- return this;
+ synchronized (syncObject) {
+ if (xaResource == null) {
+ xaResource = new FbMcXaResource();
+ }
+ return xaResource;
+ }
}
// --------------------------------------------------------------
// XAResource implementation
+ // The actual XAResource is exposed using the inner class FbMcXaResource
// --------------------------------------------------------------
// TODO validate correctness of state set
@@ -669,53 +451,43 @@ boolean isXidActive(Xid xid) {
return transaction != null && XID_ACTIVE_STATE.contains(transaction.getState());
}
- /**
- * Commits a transaction.
- *
- * @throws XAException
- * Occurs when the state was not correct (end never called), the
- * transaction ID is wrong, the connection was set to
- * Auto-Commit, or the commit on the underlying connection
- * fails. The error code differs depending on the exact
- * situation.
- */
- public void commit(Xid id, boolean onePhase) throws XAException {
- try {
- mcf.notifyCommit(this, id, onePhase);
- } catch (GDSException ge) {
- throw new XAException(ge.getXAErrorCode());
- }
+ private void commit(Xid id, boolean onePhase) throws XAException {
+ mcf.notifyCommit(this, id, onePhase);
}
/**
- * The internalCommit
method performs the requested commit
- * and may throw a GDSException to be interpreted by the caller.
+ * The {@code internalCommit} method performs the requested commit and may throw an XAException to be interpreted
+ * by the caller.
*
* @param xid
- * a Xid
value
+ * a {@code Xid} value
* @param onePhase
- * a boolean
value
- * @exception XAException
- * if an error occurs
+ * a {@code true} if this is not a two-phase commit (not a distributed transaction)
+ * @throws XAException
+ * if an error occurs
*/
void internalCommit(Xid xid, boolean onePhase) throws XAException {
if (log.isTraceEnabled()) log.trace("Commit called: " + xid);
FbTransaction committingTr = xidMap.get(xid);
// check that prepare has NOT been called when onePhase = true
- if (onePhase && isPrepared(xid))
+ if (onePhase && isPrepared(xid)) {
throw new FBXAException("Cannot commit one-phase when transaction has been prepared", XAException.XAER_PROTO);
+ }
// check that prepare has been called when onePhase = false
- if (!onePhase && !isPrepared(xid))
+ if (!onePhase && !isPrepared(xid)) {
throw new FBXAException("Cannot commit two-phase when transaction has not been prepared", XAException.XAER_PROTO);
+ }
- if (committingTr == null)
+ if (committingTr == null) {
throw new FBXAException("Commit called with unknown transaction", XAException.XAER_NOTA);
+ }
try {
- if (committingTr == getGDSHelper().getCurrentTransaction())
+ if (committingTr == getGDSHelper().getCurrentTransaction()) {
throw new FBXAException("Commit called with non-ended xid", XAException.XAER_PROTO);
+ }
committingTr.commit();
} catch (SQLException ge) {
@@ -743,48 +515,42 @@ private boolean isPrepared(Xid xid) {
* Dissociates a resource from a global transaction.
*
* @throws XAException
- * Occurs when the state was not correct (end called twice), or
- * the transaction ID is wrong.
+ * Occurs when the state was not correct (end called twice), or the transaction ID is wrong.
*/
- public void end(Xid id, int flags) throws XAException {
+ private void end(Xid id, int flags) throws XAException {
if (flags != XAResource.TMSUCCESS && flags != XAResource.TMFAIL && flags != XAResource.TMSUSPEND)
throw new FBXAException("flag not allowed in this context: " + flags + ", valid flags are TMSUCCESS, TMFAIL, TMSUSPEND", XAException.XAER_PROTO);
- try {
- internalEnd(id, flags);
- } catch (SQLException e) {
- throw new FBXAException(XAException.XAER_RMERR, e);
- }
+ internalEnd(id, flags);
mcf.notifyEnd(this, id);
inDistributedTransaction = false;
try {
// This will reset the managed environment of the associated connections and set the transaction coordinator to local
- // TODO This is a bit of a hack; need to find a better way; this doesn't work with connectionSharing = true
+ // TODO This is a bit of a hack; need to find a better way
setManagedEnvironment(isManagedEnvironment());
- } catch (ResourceException ex) {
+ } catch (SQLException ex) {
throw new FBXAException("Reset of managed state failed", XAException.XAER_RMERR, ex);
}
}
/**
- * The internalEnd
method ends the xid as requested if
- * appropriate and throws a GDSException including the appropriate XA error
- * code and a message if not. The caller can decode the exception as
- * necessary.
+ * The {@code internalEnd} method ends the xid as requested if appropriate and throws a XAException including the
+ * appropriate XA error code and a message if not. The caller can decode the exception as necessary.
*
* @param xid
- * a Xid
value
+ * a {@code Xid} value
* @param flags
- * an int
value
- * @exception XAException
- * if an error occurs
+ * an {@code int} value
+ * @throws XAException
+ * if an error occurs
*/
- void internalEnd(Xid xid, int flags) throws XAException, SQLException {
+ void internalEnd(Xid xid, int flags) throws XAException {
if (log.isDebugEnabled()) log.debug("End called: " + xid);
FbTransaction endingTr = xidMap.get(xid);
- if (endingTr == null)
+ if (endingTr == null) {
throw new FBXAException("Unrecognized transaction", XAException.XAER_NOTA);
+ }
if (flags == XAResource.TMFAIL) {
try {
@@ -793,26 +559,25 @@ void internalEnd(Xid xid, int flags) throws XAException, SQLException {
} catch (SQLException ex) {
throw new FBXAException("can't rollback transaction", XAException.XAER_RMFAIL, ex);
}
- }
- else if (flags == XAResource.TMSUCCESS) {
- if (gdsHelper != null && endingTr == gdsHelper.getCurrentTransaction())
+ } else if (flags == XAResource.TMSUCCESS) {
+ if (gdsHelper != null && endingTr == gdsHelper.getCurrentTransaction()) {
gdsHelper.setCurrentTransaction(null);
- else
+ } else {
throw new FBXAException("You are trying to end a transaction that is not the current transaction",
XAException.XAER_INVAL);
- }
- else if (flags == XAResource.TMSUSPEND) {
- if (gdsHelper != null && endingTr == gdsHelper.getCurrentTransaction())
+ }
+ } else if (flags == XAResource.TMSUSPEND) {
+ if (gdsHelper != null && endingTr == gdsHelper.getCurrentTransaction()) {
gdsHelper.setCurrentTransaction(null);
- else
+ } else {
throw new FBXAException("You are trying to suspend a transaction that is not the current transaction",
XAException.XAER_INVAL);
-
+ }
}
}
private final static String FORGET_FIND_QUERY = "SELECT RDB$TRANSACTION_ID, RDB$TRANSACTION_DESCRIPTION "
- + "FROM RDB$TRANSACTIONS WHERE RDB$TRANSACTION_STATE IN (2, 3)";
+ + "FROM RDB$TRANSACTIONS WHERE RDB$TRANSACTION_STATE IN (2, 3)";
private final static String FORGET_DELETE_QUERY = "DELETE FROM RDB$TRANSACTIONS WHERE RDB$TRANSACTION_ID = ";
/**
@@ -821,10 +586,10 @@ else if (flags == XAResource.TMSUSPEND) {
* called after a failed commit or rollback.
*
* @throws XAException
- * Occurs when the state was not correct (end never called), or
- * the transaction ID is wrong.
+ * Occurs when the state was not correct (end never called), or the transaction ID is wrong.
*/
- public void forget(Xid id) throws XAException {
+ private void forget(Xid id) throws XAException {
+ // TODO Should this method call FBManagedConnectionFactory.forget?
long inLimboId = -1;
try {
@@ -850,7 +615,7 @@ public void forget(Xid id) throws XAException {
FBField field1 = FBField.createField(stmtHandle2.getRowDescriptor().getFieldDescriptor(1), dataProvider1, gdsHelper2, false);
int row = 0;
- while(row < dataProvider0.getRowCount()) {
+ while (row < dataProvider0.getRowCount()) {
dataProvider0.setRow(row);
dataProvider1.setRow(row);
@@ -867,7 +632,7 @@ public void forget(Xid id) throws XAException {
inLimboId = inLimboTxId;
break;
}
- } catch(FBIncorrectXidException ex) {
+ } catch (FBIncorrectXidException ex) {
String message = "incorrect XID format in RDB$TRANSACTIONS where RDB$TRANSACTION_ID=" + inLimboTxId;
log.warn(message + ": " + ex + "; see debug level for stacktrace");
log.debug(message, ex);
@@ -883,8 +648,9 @@ public void forget(Xid id) throws XAException {
throw new FBXAException(XAException.XAER_RMFAIL, ex);
}
- if (inLimboId == -1)
+ if (inLimboId == -1) {
throw new FBXAException("XID not found", XAException.XAER_NOTA); // TODO: is XAER_NOTA the proper error code ?
+ }
try {
// delete XID
@@ -903,53 +669,31 @@ public void forget(Xid id) throws XAException {
}
}
- /**
- * Gets the transaction timeout.
- */
- public int getTransactionTimeout() throws javax.transaction.xa.XAException {
+ private int getTransactionTimeout() throws XAException {
return timeout;
}
- /**
- * Retrieve whether this FBManagedConnection
uses the same
- * ResourceManager as res
. This method relies on
- * res
being a Firebird implementation of
- * XAResource
.
- *
- * @param res
- * The other XAResource
to compare to
- * @return true
if res
uses the same
- * ResourceManager, false
otherwise
- */
- public boolean isSameRM(XAResource res) throws XAException {
- return res instanceof FBManagedConnection
- && database == ((FBManagedConnection) res).database;
- }
-
/**
* Prepares a transaction to commit.
*
* @throws XAException
- * Occurs when the state was not correct (end never called), the
- * transaction ID is wrong, or the connection was set to
- * Auto-Commit.
+ * Occurs when the state was not correct (end never called), the transaction ID is wrong, or the connection
+ * was set to Auto-Commit.
*/
- public int prepare(Xid xid) throws javax.transaction.xa.XAException {
- try {
- return mcf.notifyPrepare(this, xid);
- } catch (GDSException ge) {
- throw new FBXAException(XAException.XAER_RMERR, ge);
- }
+ private int prepare(Xid xid) throws XAException {
+ return mcf.notifyPrepare(this, xid);
}
int internalPrepare(Xid xid) throws FBXAException {
if (log.isTraceEnabled()) log.trace("prepare called: " + xid);
FbTransaction committingTr = xidMap.get(xid);
- if (committingTr == null)
+ if (committingTr == null) {
throw new FBXAException("Prepare called with unknown transaction", XAException.XAER_NOTA);
+ }
try {
- if (committingTr == getGDSHelper().getCurrentTransaction())
+ if (committingTr == getGDSHelper().getCurrentTransaction()) {
throw new FBXAException("Prepare called with non-ended xid", XAException.XAER_PROTO);
+ }
FBXid fbxid;
if (xid instanceof FBXid) {
@@ -978,11 +722,10 @@ int internalPrepare(Xid xid) throws FBXAException {
}
preparedXid.add(xid);
- return XA_OK;
+ return XAResource.XA_OK;
}
- private static final String RECOVERY_QUERY =
- "SELECT RDB$TRANSACTION_ID, RDB$TRANSACTION_DESCRIPTION "
+ private static final String RECOVERY_QUERY = "SELECT RDB$TRANSACTION_ID, RDB$TRANSACTION_DESCRIPTION "
+ "FROM RDB$TRANSACTIONS";
/**
@@ -992,20 +735,18 @@ int internalPrepare(Xid xid) throws FBXAException {
* heuristically completed states.
*
* @param flags
- * One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be
- * used when no other flags are set in flags.
- * @return The resource manager returns zero or more XIDs for the
- * transaction branches that are currently in a prepared or
- * heuristically completed state. If an error occurs during the
- * operation, the resource manager should throw the appropriate
- * XAException.
+ * One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be used when no other flags are set in flags.
+ * @return The resource manager returns zero or more XIDs for the transaction branches that are currently in a
+ * prepared or heuristically completed state. If an error occurs during the operation, the resource manager should
+ * throw the appropriate XAException.
* @throws XAException
- * An error has occurred. Possible values are XAER_RMERR,
- * XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
+ * An error has occurred. Possible values are XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
*/
- public Xid[] recover(int flags) throws javax.transaction.xa.XAException {
- if (flags != XAResource.TMSTARTRSCAN && flags != XAResource.TMENDRSCAN && flags != XAResource.TMNOFLAGS && flags != (XAResource.TMSTARTRSCAN|XAResource.TMENDRSCAN))
+ private Xid[] recover(int flags) throws javax.transaction.xa.XAException {
+ if (flags != XAResource.TMSTARTRSCAN && flags != XAResource.TMENDRSCAN && flags != XAResource.TMNOFLAGS
+ && flags != (XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN)) {
throw new FBXAException("flag not allowed in this context: " + flags + ", valid flags are TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS, TMSTARTRSCAN|TMENDRSCAN", XAException.XAER_PROTO);
+ }
try {
// if (!((flags & XAResource.TMSTARTRSCAN) == 0))
@@ -1035,7 +776,7 @@ public Xid[] recover(int flags) throws javax.transaction.xa.XAException {
FBField field1 = FBField.createField(stmtHandle2.getRowDescriptor().getFieldDescriptor(1), dataProvider1, gdsHelper2, false);
int row = 0;
- while(row < dataProvider0.getRowCount()) {
+ while (row < dataProvider0.getRowCount()) {
dataProvider0.setRow(row);
dataProvider1.setRow(row);
@@ -1045,7 +786,7 @@ public Xid[] recover(int flags) throws javax.transaction.xa.XAException {
try {
FBXid xid = new FBXid(new ByteArrayInputStream(inLimboMessage), inLimboTxId);
xids.add(xid);
- } catch(FBIncorrectXidException ex) {
+ } catch (FBIncorrectXidException ex) {
log.warn("ignoring XID stored with invalid format in RDB$TRANSACTIONS for RDB$TRANSACTION_ID=" + inLimboTxId);
}
@@ -1070,11 +811,11 @@ public Xid[] recover(int flags) throws javax.transaction.xa.XAException {
* Obtain a single prepared transaction branch from a resource manager, based on a Xid
*
* @param externalXid
- * The Xid to find
+ * The Xid to find
* @return The Xid if found, otherwise null.
* @throws XAException
- * An error has occurred. Possible values are XAER_RMERR,
- * XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
+ * An error has occurred. Possible values are XAER_RMERR,
+ * XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
*/
protected Xid findSingleXid(Xid externalXid) throws javax.transaction.xa.XAException {
try {
@@ -1111,7 +852,7 @@ protected Xid findSingleXid(Xid externalXid) throws javax.transaction.xa.XAExcep
try {
xid = new FBXid(new ByteArrayInputStream(inLimboMessage), inLimboTxId);
- } catch(FBIncorrectXidException ex) {
+ } catch (FBIncorrectXidException ex) {
log.warn("ignoring XID stored with invalid format in RDB$TRANSACTIONS for RDB$TRANSACTION_ID=" + inLimboTxId);
}
}
@@ -1166,25 +907,19 @@ public void receivedRow(FbStatement sender, RowValue rowValue) {
* transaction.
*
* @throws XAException
- * Occurs when the state was not correct (end never called), the
- * transaction ID is wrong, the connection was set to
- * Auto-Commit, or the rollback on the underlying connection
- * fails. The error code differs depending on the exact
- * situation.
+ * Occurs when the state was not correct (end never called), the transaction ID is wrong, the connection
+ * was set to Auto-Commit, or the rollback on the underlying connection fails. The error code differs
+ * depending on the exact situation.
*/
- public void rollback(Xid xid) throws XAException {
- try {
- mcf.notifyRollback(this, xid);
- } catch (GDSException ge) {
- throw new FBXAException(ge.getXAErrorCode(), ge);
- }
+ private void rollback(Xid xid) throws XAException {
+ mcf.notifyRollback(this, xid);
}
void internalRollback(Xid xid) throws XAException {
if (log.isTraceEnabled()) log.trace("rollback called: " + xid);
FbTransaction committingTr = xidMap.get(xid);
if (committingTr == null) {
- throw new FBXAException ("Rollback called with unknown transaction: " + xid);
+ throw new FBXAException("Rollback called with unknown transaction: " + xid);
}
try {
@@ -1208,10 +943,9 @@ void internalRollback(Xid xid) throws XAException {
* the current implementation.
*
* @param timeout
- * The timeout to be set in seconds
+ * The timeout to be set in seconds
*/
- public boolean setTransactionTimeout(int timeout)
- throws javax.transaction.xa.XAException {
+ private boolean setTransactionTimeout(int timeout) throws XAException {
this.timeout = timeout;
return true;
}
@@ -1229,22 +963,21 @@ public boolean inDistributedTransaction() {
* transaction ID or illegal transaction ID (since you can't have two
* transactions associated with one DB connection).
*
- *
* @param id
- * A global transaction identifier to be associated with the
- * resource
+ * A global transaction identifier to be associated with the resource
* @param flags
- * One of TMNOFLAGS, TMJOIN, or TMRESUME
+ * One of TMNOFLAGS, TMJOIN, or TMRESUME
* @throws XAException
- * Occurs when the state was not correct (start called twice),
- * the transaction ID is wrong, or the instance has already been
- * closed.
+ * Occurs when the state was not correct (start called twice), the transaction ID is wrong, or the instance
+ * has already been closed.
*/
- public void start(Xid id, int flags) throws XAException {
- if (flags != XAResource.TMNOFLAGS && flags != XAResource.TMJOIN && flags != XAResource.TMRESUME)
+ private void start(Xid id, int flags) throws XAException {
+ if (flags != XAResource.TMNOFLAGS && flags != XAResource.TMJOIN && flags != XAResource.TMRESUME) {
throw new FBXAException("flag not allowed in this context: " + flags + ", valid flags are TMNOFLAGS, TMJOIN, TMRESUME", XAException.XAER_PROTO);
- if (flags == XAResource.TMJOIN)
+ }
+ if (flags == XAResource.TMJOIN) {
throw new FBXAException("Joining two transactions is not supported", XAException.XAER_RMFAIL);
+ }
try {
// reset the transaction parameters for the managed scenario
@@ -1257,12 +990,10 @@ public void start(Xid id, int flags) throws XAException {
inDistributedTransaction = true;
// This will reset the managed environment of the associated connections and set the transaction coordinator to managed
- // TODO This is a bit of a hack; need to find a better way; this doesn't work with connectionSharing = true
+ // TODO This is a bit of a hack; need to find a better way
setManagedEnvironment(isManagedEnvironment());
- } catch (GDSException ge) {
- throw new FBXAException(ge.getXAErrorCode(), ge);
- } catch (SQLException | ResourceException e) {
+ } catch (SQLException e) {
throw new FBXAException(XAException.XAER_RMERR, e);
}
}
@@ -1271,14 +1002,15 @@ public void start(Xid id, int flags) throws XAException {
* Perform the internal processing to start associate a JDBC connection with
* a global transaction.
*
- * @see #start(Xid, int)
* @param id
- * A global transaction identifier to be associated with the
- * resource
+ * A global transaction identifier to be associated with the resource
* @param flags
- * One of TMNOFLAGS, TMJOIN, or TMRESUME
- * @throws XAException If the transaction is already started, or this connection cannot participate in the distributed transaction
+ * One of TMNOFLAGS, TMJOIN, or TMRESUME
+ * @throws XAException
+ * If the transaction is already started, or this connection cannot participate in the distributed
+ * transaction
* @throws SQLException
+ * @see #start(Xid, int)
*/
public void internalStart(Xid id, int flags) throws XAException, SQLException {
if (log.isTraceEnabled()) log.trace("start called: " + id);
@@ -1292,17 +1024,18 @@ public void internalStart(Xid id, int flags) throws XAException, SQLException {
// FB public methods. Could be package if packages reorganized.
/**
- * Close this connection with regards to a wrapping
- * AbstractConnection
.
+ * Close this connection with regards to a wrapping {@code AbstractConnection}.
*
* @param c
- * The AbstractConnection
that is being closed
+ * The {@code AbstractConnection} that is being closed
*/
public void close(FBConnection c) {
c.setManagedConnection(null);
- connectionHandles.remove(c);
- ConnectionEvent ce = new ConnectionEvent(this,
- ConnectionEvent.CONNECTION_CLOSED, null);
+ if (!connectionHandleUpdater.compareAndSet(this, c, null) && log.isDebugEnabled()) {
+ log.debug("Call of close for connection not currently associated with this managed connection",
+ new RuntimeException("debug call trace"));
+ }
+ XcaConnectionEvent ce = new XcaConnectionEvent(this, XcaConnectionEvent.EventType.CONNECTION_CLOSED);
ce.setConnectionHandle(c);
notify(connectionClosedNotifier, ce);
}
@@ -1332,10 +1065,6 @@ public void setTransactionParameters(int isolation, TransactionParameterBuffer t
mcf.setTransactionParameters(isolation, transactionParams);
}
- // --------------------------------------------------------------------
- // package visibility
- // --------------------------------------------------------------------
-
private void findIscTrHandle(Xid xid, int flags) throws SQLException, XAException {
// FIXME return old tr handle if it is still valid before proceeding
getGDSHelper().setCurrentTransaction(null);
@@ -1371,115 +1100,59 @@ private void findIscTrHandle(Xid xid, int flags) throws SQLException, XAExceptio
}
}
- void notify(CELNotifier notifier, ConnectionEvent ce) {
- for (ConnectionEventListener cel : connectionEventListeners) {
+ void notify(CELNotifier notifier, XcaConnectionEvent ce) {
+ for (XcaConnectionEventListener cel : connectionEventListeners) {
notifier.notify(cel, ce);
}
}
+ @FunctionalInterface
interface CELNotifier {
-
- void notify(ConnectionEventListener cel, ConnectionEvent ce);
+ void notify(XcaConnectionEventListener cel, XcaConnectionEvent ce);
}
- static final CELNotifier connectionClosedNotifier = new CELNotifier() {
-
- public void notify(ConnectionEventListener cel, ConnectionEvent ce) {
- cel.connectionClosed(ce);
- }
- };
-
- static final CELNotifier connectionErrorOccurredNotifier = new CELNotifier() {
-
- public void notify(ConnectionEventListener cel, ConnectionEvent ce) {
- cel.connectionErrorOccurred(ce);
- }
- };
-
- static final CELNotifier localTransactionStartedNotifier = new CELNotifier() {
-
- public void notify(ConnectionEventListener cel, ConnectionEvent ce) {
- cel.localTransactionStarted(ce);
- }
- };
-
- static final CELNotifier localTransactionCommittedNotifier = new CELNotifier() {
-
- public void notify(ConnectionEventListener cel, ConnectionEvent ce) {
- cel.localTransactionCommitted(ce);
- }
- };
-
- static final CELNotifier localTransactionRolledbackNotifier = new CELNotifier() {
-
- public void notify(ConnectionEventListener cel, ConnectionEvent ce) {
- cel.localTransactionRolledback(ce);
- }
- };
-
- boolean matches(Subject subj, ConnectionRequestInfo cri) {
-
- if (cri == null) {
- return true;
- }
-
- if (!(cri instanceof FBConnectionRequestInfo))
- return false;
-
- try {
- return this.cri.equals(getCombinedConnectionRequestInfo(subj, cri));
- } catch (ResourceException re) {
- return false;
- }
- }
+ static final CELNotifier connectionClosedNotifier = XcaConnectionEventListener::connectionClosed;
+ static final CELNotifier connectionErrorOccurredNotifier = XcaConnectionEventListener::connectionErrorOccurred;
/**
- * Get the transaction isolation level of this connection. The level is one
- * of the static final fields of java.sql.Connection
(i.e.
- * TRANSACTION_READ_COMMITTED
,
- * TRANSACTION_READ_UNCOMMITTED
,
- * TRANSACTION_REPEATABLE_READ
,
- * TRANSACTION_SERIALIZABLE
.
+ * Get the transaction isolation level of this connection. The level is one of the static final fields of
+ * {@code java.sql.Connection} (i.e. {@code TRANSACTION_READ_COMMITTED}, {@code TRANSACTION_READ_UNCOMMITTED},
+ * {@code TRANSACTION_REPEATABLE_READ}, {@code TRANSACTION_SERIALIZABLE}.
*
+ * @return Value representing a transaction isolation level defined in {@link java.sql.Connection}.
+ * @throws SQLException
+ * If the transaction level cannot be retrieved
* @see java.sql.Connection
* @see #setTransactionIsolation(int)
- * @return Value representing a transaction isolation level defined in
- * {@link java.sql.Connection}.
- * @throws ResourceException
- * If the transaction level cannot be retrieved
*/
- public int getTransactionIsolation() throws ResourceException {
+ public int getTransactionIsolation() throws SQLException {
return transactionIsolation;
}
/**
- * Set the transaction level for this connection. The level is one of the
- * static final fields of java.sql.Connection
(i.e.
- * TRANSACTION_READ_COMMITTED
,
- * TRANSACTION_READ_UNCOMMITTED
,
- * TRANSACTION_REPEATABLE_READ
,
- * TRANSACTION_SERIALIZABLE
.
+ * Set the transaction level for this connection. The level is one of the static final fields of
+ * {@code java.sql.Connection} (i.e. {@code TRANSACTION_READ_COMMITTED}, {@code TRANSACTION_READ_UNCOMMITTED},
+ * {@code TRANSACTION_REPEATABLE_READ}, {@code TRANSACTION_SERIALIZABLE}.
*
+ * @param isolation
+ * Value representing a transaction isolation level defined in {@link java.sql.Connection}.
+ * @throws SQLException
+ * If the transaction level cannot be retrieved
* @see java.sql.Connection
* @see #getTransactionIsolation()
- * @param isolation
- * Value representing a transaction isolation level defined in
- * {@link java.sql.Connection}.
- * @throws ResourceException
- * If the transaction level cannot be retrieved
*/
- public void setTransactionIsolation(int isolation) throws ResourceException {
+ public void setTransactionIsolation(int isolation) throws SQLException {
transactionIsolation = isolation;
-
+
tpb = mcf.getTpb(isolation);
}
/**
* Get the managed connection factory that created this managed connection.
*
- * @return instance of {@link ManagedConnectionFactory}.
+ * @return instance of {@link FBManagedConnectionFactory}.
*/
- public ManagedConnectionFactory getManagedConnectionFactory() {
+ public FBManagedConnectionFactory getManagedConnectionFactory() {
return mcf;
}
@@ -1487,8 +1160,7 @@ public ManagedConnectionFactory getManagedConnectionFactory() {
* Set whether this connection is to be readonly
*
* @param readOnly
- * If true
, the connection will be set read-only,
- * otherwise it will be writable
+ * If {@code true}, the connection will be set read-only, otherwise it will be writable
*/
public void setReadOnly(boolean readOnly) {
tpb.setReadOnly(readOnly);
@@ -1497,25 +1169,32 @@ public void setReadOnly(boolean readOnly) {
/**
* Retrieve whether this connection is readonly.
*
- * @return true
if this connection is readonly,
- * false
otherwise
+ * @return {@code true} if this connection is readonly, {@code false} otherwise
*/
public boolean isReadOnly() {
return tpb.isReadOnly();
}
private void notifyWarning(SQLWarning warning) {
- synchronized (connectionHandles) {
- if (connectionHandles.isEmpty()) {
- if (unnotifiedWarnings == null) {
- unnotifiedWarnings = warning;
- } else {
- unnotifiedWarnings.setNextWarning(warning);
+ final FBConnection connection = connectionHandle;
+ if (connection == null) {
+ while (true) {
+ if (!unnotifiedWarningsUpdater.compareAndSet(this, null, warning)) {
+ final SQLWarning warnings = unnotifiedWarnings;
+ if (warnings == null) {
+ continue;
+ }
+ warnings.setNextWarning(warning);
}
+ break;
}
- for (FBConnection connection : connectionHandles) {
- connection.addWarning(warning);
+ } else {
+ final SQLWarning warnings = unnotifiedWarningsUpdater.getAndSet(this, null);
+ if (warnings != null) {
+ warnings.setNextWarning(warning);
+ warning = warnings;
}
+ connection.addWarning(warning);
}
}
@@ -1536,7 +1215,7 @@ private static String getDefaultConnectionEncoding() {
}
/**
- * DatabaseListener implementation for use by this ManagedConnection.
+ * DatabaseListener implementation for use by this managed connection.
*/
private class MCDatabaseListener extends DefaultDatabaseListener {
@Override
@@ -1548,4 +1227,73 @@ public void warningReceived(FbDatabase database, SQLWarning warning) {
notifyWarning(warning);
}
}
+
+ /**
+ * XAResource implementation that delegates to the managed connection itself.
+ */
+ private final class FbMcXaResource implements XAResource {
+
+ private FBManagedConnection getMc() {
+ return FBManagedConnection.this;
+ }
+
+ @Override
+ public void start(Xid xid, int flags) throws XAException {
+ FBManagedConnection.this.start(xid, flags);
+ }
+
+ @Override
+ public int prepare(Xid xid) throws XAException {
+ return FBManagedConnection.this.prepare(xid);
+ }
+
+ @Override
+ public void commit(Xid xid, boolean onePhase) throws XAException {
+ FBManagedConnection.this.commit(xid, onePhase);
+ }
+
+ @Override
+ public void rollback(Xid xid) throws XAException {
+ FBManagedConnection.this.rollback(xid);
+ }
+
+ @Override
+ public void end(Xid xid, int flags) throws XAException {
+ FBManagedConnection.this.end(xid, flags);
+ }
+
+ @Override
+ public void forget(Xid xid) throws XAException {
+ FBManagedConnection.this.forget(xid);
+ }
+
+ @Override
+ public Xid[] recover(int flag) throws XAException {
+ return FBManagedConnection.this.recover(flag);
+ }
+
+ /**
+ * Retrieve whether this {@code XAResource} uses the same ResourceManager as {@code res}. This method relies on
+ * {@code res} being a Firebird implementation of {@code XAResource}.
+ *
+ * @param res
+ * The other {@code XAResource} to compare to
+ * @return {@code true} if {@code res} uses the same ResourceManager, {@code false} otherwise
+ */
+ @Override
+ public boolean isSameRM(XAResource res) throws XAException {
+ return res == this
+ || res instanceof FbMcXaResource && database == ((FbMcXaResource) res).getMc().database;
+ }
+
+ @Override
+ public int getTransactionTimeout() throws XAException {
+ return FBManagedConnection.this.getTransactionTimeout();
+ }
+
+ @Override
+ public boolean setTransactionTimeout(int seconds) throws XAException {
+ return FBManagedConnection.this.setTransactionTimeout(seconds);
+ }
+ }
}
diff --git a/src/main/org/firebirdsql/jaybird/xca/FBManagedConnectionFactory.java b/src/main/org/firebirdsql/jaybird/xca/FBManagedConnectionFactory.java
index 5df4739503..194febc601 100644
--- a/src/main/org/firebirdsql/jaybird/xca/FBManagedConnectionFactory.java
+++ b/src/main/org/firebirdsql/jaybird/xca/FBManagedConnectionFactory.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -19,7 +19,6 @@
package org.firebirdsql.jaybird.xca;
import org.firebirdsql.gds.DatabaseParameterBuffer;
-import org.firebirdsql.gds.GDSException;
import org.firebirdsql.gds.ISCConstants;
import org.firebirdsql.gds.TransactionParameterBuffer;
import org.firebirdsql.gds.impl.GDSFactory;
@@ -29,21 +28,14 @@
import org.firebirdsql.gds.ng.FbStatement;
import org.firebirdsql.gds.ng.FbTransaction;
import org.firebirdsql.gds.ng.fields.RowValue;
-import org.firebirdsql.jdbc.FBConnection;
-import org.firebirdsql.jdbc.FBConnectionProperties;
-import org.firebirdsql.jdbc.FBDataSource;
-import org.firebirdsql.jdbc.FirebirdConnectionProperties;
-
-import javax.resource.NotSupportedException;
-import javax.resource.ResourceException;
-import javax.resource.spi.SecurityException;
-import javax.resource.spi.*;
-import javax.security.auth.Subject;
+import org.firebirdsql.jdbc.*;
+
+import javax.sql.DataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
-import java.io.ObjectStreamException;
-import java.io.PrintWriter;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
@@ -52,32 +44,27 @@
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
- * FBManagedConnectionFactory implements the jca ManagedConnectionFactory
- * interface and also many of the internal functions of ManagedConnection. This
- * nonstandard behavior is required due to firebird requiring all work done in a
- * transaction to be done over one connection. To support xa semantics, the
- * correct db handle must be located whenever a ManagedConnection is associated
- * with an xid.
- *
- * WARNING: this adapter will probably not work properly in an environment where
- * ManagedConnectionFactory is serialized and deserialized, and the deserialized
- * copy is expected to function as anything other than a key.
- *
+ * FBManagedConnectionFactory is a factory for {@link FBManagedConnection}, and implements many of the internal
+ * functions of FBManagedConnection. This behavior is required due to firebird requiring all work done in a transaction
+ * to be done over one connection.
+ * mcfInstances
weak hash map is used in deserialization
- * to find the correct instance of a mcf after deserializing.
+ * The {@code mcfInstances} weak hash map is used in deserialization to find the correct instance of a mcf after
+ * deserializing.
* createConnectionFactory
method creates a DataSource
- * using the supplied ConnectionManager.
- *
- * @param cxManager
- * a ConnectionManager
value
- * @return a java.lang.Object
value
- * @exception ResourceException
- * if an error occurs
+ * Creates a {@code javax.sql.DataSource} instance. The data source instance gets initialized with the passed
+ * XcaConnectionManager.
+ *
+ * @param connectionManager
+ * Connection manager
+ * @return data source instance
*/
- public Object createConnectionFactory(ConnectionManager cxManager)
- throws ResourceException {
+ public DataSource createConnectionFactory(XcaConnectionManager connectionManager) {
start();
- return new FBDataSource(this, cxManager);
+ return new FBDataSource(this, connectionManager);
}
/**
- * The createConnectionFactory
method creates a DataSource
- * with a default stand alone ConnectionManager. Ours can implement pooling.
- *
- * @return a new javax.sql.DataSource
based around this
- * connection factory
- * @exception ResourceException
- * if an error occurs
+ * Creates a {@code javax.sql.DataSource} instance. The data source instance gets initialized with a default
+ * XcaConnectionManager provided by the resource adapter.
+ *
+ * @return data source instance
*/
- public Object createConnectionFactory() throws ResourceException {
- start();
- return new FBDataSource(this, defaultCm);
+ public DataSource createConnectionFactory() {
+ return createConnectionFactory(defaultCm);
}
/**
- * Creates a new physical connection to the underlying EIS resource manager,
- * ManagedConnectionFactory uses the security information (passed as
- * Subject) and additional ConnectionRequestInfo (which is specific to
- * ResourceAdapter and opaque to application server) to create this new
- * connection.
- *
- * @param subject
- * Caller's security information
- * @param cri
- * Additional resource adapter specific connection request
- * information
- * @return ManagedConnection instance
- * @throws ResourceException
- * generic exception
- * @throws SecurityException
- * security related error
- * @throws ResourceAllocationException
- * failed to allocate system resources for connection request
- * @throws ResourceAdapterInternalException
- * resource adapter related error condition
- * @throws EISSystemException
- * internal error condition in EIS instance
+ * Creates a new physical connection to the Firebird database using the default configuration.
+ *
+ * @return Managed connection instance
+ * @throws SQLException
+ * generic exception
+ * @see #createManagedConnection(FBConnectionRequestInfo)
*/
- public ManagedConnection createManagedConnection(Subject subject,
- ConnectionRequestInfo cri) throws ResourceException {
+ public FBManagedConnection createManagedConnection() throws SQLException {
start();
- return new FBManagedConnection(subject, cri, this);
- }
-
- /**
- * Returns a matched connection from the candidate set of connections.
- * ManagedConnectionFactory uses the security info (as in
- * Subject
) and information provided through
- * ConnectionRequestInfo
and additional Resource Adapter
- * specific criteria to do matching. Note that criteria used for matching is
- * specific to a resource adapter and is not prescribed by the
- * Connector
specification.
- * ManagedConnection
instance that is
- * the best match for handling the connection allocation request.
- *
- * @param connectionSet
- * candidate connection set
- * @param subject
- * caller's security information
- * @param cxRequestInfo
- * additional resource adapter specific connection request
- * information
- * @return ManagedConnection if resource adapter finds an acceptable match
- * otherwise null
- * @throws ResourceException -
- * generic exception
- * @throws SecurityException -
- * security related error
- * @throws ResourceAdapterInternalException -
- * resource adapter related error condition
- * @throws NotSupportedException -
- * if operation is not supported
- */
- public ManagedConnection matchManagedConnections(Set connectionSet, javax.security.auth.Subject subject,
- ConnectionRequestInfo cxRequestInfo) throws ResourceException {
-
- for (Object connection : connectionSet) {
- if (!(connection instanceof FBManagedConnection)) continue;
- FBManagedConnection mc = (FBManagedConnection) connection;
-
- if (mc.matches(subject, cxRequestInfo))
- return mc;
- }
- return null;
+ return new FBManagedConnection(null, this);
}
/**
- * Set the log writer for this ManagedConnectionFactory
- * instance. The log writer is a character output stream to which all
- * logging and tracing messages for this
- * ManagedConnectionFactory
instance will be printed.
- * ApplicationServer manages the association of output stream with the
- * ManagedConnectionFactory
. When a
- * ManagedConnectionFactory
object is created the log writer
- * is initially null
, in other words, logging is disabled.
- * Once a log writer is associated with a
- * ManagedConnectionFactory
, logging and tracing for
- * ManagedConnectionFactory
instance is enabled.
+ * Creates a new physical connection to the Firebird database.
* ManagedConnection
instances created by
- * ManagedConnectionFactory
"inherits" the log writer, which
- * can be overridden by ApplicationServer using
- * {@link ManagedConnection#setLogWriter}to set
- * ManagedConnection
specific logging and tracing.
- *
- * @param out
- * an out stream for error logging and tracing
- * @throws ResourceException
- * generic exception
- * @throws ResourceAdapterInternalException
- * resource adapter related error condition
+ * ManagedConnectionFactory uses the additional ConnectionRequestInfo to create this new connection.
+ * ManagedConnectionFactory
- * instance. The log writer is a character output stream to which all
- * logging and tracing messages for this
- * ManagedConnectionFactory
instance will be printed.
- * ApplicationServer manages the association of output stream with the
- * ManagedConnectionFactory
. When a
- * ManagedConnectionFactory
object is created the log writer
- * is initially null, in other words, logging is disabled.
- *
- * @return PrintWriter instance
- * @throws ResourceException
- * generic exception
- */
- public PrintWriter getLogWriter() {
- return null;// we are using alternative logging
+ private void readObject(ObjectInputStream stream) throws InvalidObjectException {
+ throw new InvalidObjectException("Serialization proxy required");
}
- // Serialization support
- private Object readResolve() throws ObjectStreamException {
- FBManagedConnectionFactory mcf = internalCanonicalize();
- if (mcf != null) return mcf;
-
- mcf = new FBManagedConnectionFactory(getGDSType(), (FBConnectionProperties)this.connectionProperties.clone());
- return mcf;
+ protected Object writeReplace() {
+ return new SerializationProxy(this);
}
/**
- * The canonicalize
method is used in FBDriver to reuse
- * previous fbmcf instances if they have been create. It should really be
- * package access level
- *
- * @return a FBManagedConnectionFactory
value
+ * The {@code canonicalize} method is used in FBDriver to reuse previous fbmcf instances if they have been create.
+ * It should really be package access level
+ *
+ * @return a {@code FBManagedConnectionFactory} value
*/
public FBManagedConnectionFactory canonicalize() {
final FBManagedConnectionFactory mcf = internalCanonicalize();
@@ -728,7 +562,7 @@ private void cleanMcfInstances() {
}
}
- void notifyStart(FBManagedConnection mc, Xid xid) throws GDSException {
+ void notifyStart(FBManagedConnection mc, Xid xid) {
xidMap.put(xid, mc);
}
@@ -736,43 +570,45 @@ void notifyEnd(FBManagedConnection mc, Xid xid) throws XAException {
// empty
}
- int notifyPrepare(FBManagedConnection mc, Xid xid) throws GDSException, XAException {
+ int notifyPrepare(FBManagedConnection mc, Xid xid) throws XAException {
FBManagedConnection targetMc = xidMap.get(xid);
- if (targetMc == null)
+ if (targetMc == null) {
throw new FBXAException("Commit called with unknown transaction", XAException.XAER_NOTA);
+ }
return targetMc.internalPrepare(xid);
}
- void notifyCommit(FBManagedConnection mc, Xid xid, boolean onePhase) throws GDSException, XAException {
-
+ void notifyCommit(FBManagedConnection mc, Xid xid, boolean onePhase) throws XAException {
FBManagedConnection targetMc = xidMap.get(xid);
- if (targetMc == null)
+ if (targetMc == null) {
tryCompleteInLimboTransaction(xid, true);
- else
+ } else {
targetMc.internalCommit(xid, onePhase);
+ }
xidMap.remove(xid);
}
- void notifyRollback(FBManagedConnection mc, Xid xid) throws GDSException, XAException {
+ void notifyRollback(FBManagedConnection mc, Xid xid) throws XAException {
FBManagedConnection targetMc = xidMap.get(xid);
- if (targetMc == null)
+ if (targetMc == null) {
tryCompleteInLimboTransaction(xid, false);
- else
+ } else {
targetMc.internalRollback(xid);
+ }
xidMap.remove(xid);
}
- public void forget(FBManagedConnection mc, Xid xid) throws GDSException {
+ public void forget(FBManagedConnection mc, Xid xid) {
xidMap.remove(xid);
}
- public void recover(FBManagedConnection mc, Xid xid) throws GDSException {
+ public void recover(FBManagedConnection mc, Xid xid) {
}
@@ -781,23 +617,21 @@ public void recover(FBManagedConnection mc, Xid xid) throws GDSException {
* reconnect an "in limbo" transaction and complete it either by commit or
* rollback. If no "in limbo" transaction can be found, or error happens
* during completion, an exception is thrown.
- *
+ *
* @param xid
- * Xid of the transaction to reconnect.
+ * Xid of the transaction to reconnect.
* @param commit
- * true
if "in limbo" transaction should be
- * committed, otherwise false
.
- *
+ * {@code true} if "in limbo" transaction should be committed, otherwise {@code false}.
* @throws XAException
- * if "in limbo" transaction cannot be completed.
+ * if "in limbo" transaction cannot be completed.
*/
private void tryCompleteInLimboTransaction(Xid xid, boolean commit) throws XAException {
try {
FBManagedConnection tempMc = null;
- FirebirdLocalTransaction tempLocalTx = null;
+ FBLocalTransaction tempLocalTx = null;
try {
- tempMc = new FBManagedConnection(null, null, this);
- tempLocalTx = (FirebirdLocalTransaction) tempMc.getLocalTransaction();
+ tempMc = createManagedConnection();
+ tempLocalTx = tempMc.getLocalTransaction();
tempLocalTx.begin();
long fbTransactionId = 0;
@@ -805,7 +639,7 @@ private void tryCompleteInLimboTransaction(Xid xid, boolean commit) throws XAExc
if (tempMc.getGDSHelper().compareToVersion(2, 0) < 0) {
// Find Xid by scanning
- FBXid[] inLimboIds = (FBXid[]) tempMc.recover(XAResource.TMSTARTRSCAN);
+ FBXid[] inLimboIds = (FBXid[]) tempMc.getXAResource().recover(XAResource.TMSTARTRSCAN);
for (FBXid inLimboId : inLimboIds) {
if (inLimboId.equals(xid)) {
found = true;
@@ -880,13 +714,12 @@ private void tryCompleteInLimboTransaction(Xid xid, boolean commit) throws XAExc
if (tempMc != null) tempMc.destroy();
}
}
- } catch (ResourceException ex) {
+ } catch (SQLException ex) {
throw new FBXAException(XAException.XAER_RMERR, ex);
}
}
- FBConnection newConnection(FBManagedConnection mc)
- throws ResourceException {
+ FBConnection newConnection(FBManagedConnection mc) throws SQLException {
Class> connectionClass = GDSFactory.getConnectionClass(getGDSType());
if (!FBConnection.class.isAssignableFrom(connectionClass))
@@ -900,33 +733,63 @@ FBConnection newConnection(FBManagedConnection mc)
return (FBConnection) constructor
.newInstance(mc);
-
} catch (NoSuchMethodException ex) {
- throw new FBResourceException(
- "Cannot instantiate connection class "
- + connectionClass.getName()
- + ", no constructor accepting "
- + FBManagedConnection.class
- + " class as single parameter was found.");
+ // TODO More specific exception, Jaybird error code
+ throw new SQLException("Cannot instantiate connection class " + connectionClass.getName()
+ + ", no constructor accepting " + FBManagedConnection.class
+ + " class as single parameter was found.");
} catch (InvocationTargetException ex) {
+ final Throwable cause = ex.getCause();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ }
- if (ex.getTargetException() instanceof RuntimeException)
- throw (RuntimeException) ex.getTargetException();
+ if (cause instanceof Error) {
+ throw (Error) cause;
+ }
- if (ex.getTargetException() instanceof Error)
- throw (Error) ex.getTargetException();
+ if (cause instanceof SQLException) {
+ throw (SQLException) cause;
+ }
- throw new FBResourceException((Exception) ex.getTargetException());
+ // TODO More specific exception, Jaybird error code
+ throw new SQLException(ex.getMessage(), ex);
} catch (IllegalAccessException ex) {
- throw new FBResourceException("Constructor for class "
- + connectionClass.getName() + " is not accessible.");
+ // TODO More specific exception, Jaybird error code
+ throw new SQLException("Constructor for class " + connectionClass.getName() + " is not accessible.", ex);
} catch (InstantiationException ex) {
- throw new FBResourceException("Cannot instantiate class"
- + connectionClass.getName());
+ // TODO More specific exception, Jaybird error code
+ throw new SQLException("Cannot instantiate class" + connectionClass.getName(), ex);
}
}
public final FBConnectionProperties getCacheKey() {
return (FBConnectionProperties) connectionProperties.clone();
}
+
+ private static class SerializationProxy implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final XcaConnectionManager fbCm;
+ private final String type;
+ private final FBConnectionProperties fbConnectionProperties;
+
+ private SerializationProxy(FBManagedConnectionFactory connectionFactory) {
+ this.fbCm = connectionFactory.defaultCm;
+ this.type = connectionFactory.getType();
+ this.fbConnectionProperties = connectionFactory.connectionProperties;
+ }
+
+ protected Object readResolve() {
+ GDSType gdsType = GDSType.getType(type);
+ if (gdsType == null) {
+ gdsType = GDSFactory.getDefaultGDSType();
+ }
+ FBManagedConnectionFactory mcf = new FBManagedConnectionFactory(gdsType, fbConnectionProperties);
+ mcf.setDefaultConnectionManager(fbCm);
+ FBManagedConnectionFactory canonicalizedMcf = mcf.internalCanonicalize();
+ return canonicalizedMcf != null ? canonicalizedMcf : mcf;
+ }
+ }
}
diff --git a/src/main/org/firebirdsql/jaybird/xca/FBManagedConnectionMetaData.java b/src/main/org/firebirdsql/jaybird/xca/FBManagedConnectionMetaData.java
deleted file mode 100644
index 7c8dd7974c..0000000000
--- a/src/main/org/firebirdsql/jaybird/xca/FBManagedConnectionMetaData.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
- *
- * Distributable under LGPL license.
- * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * LGPL License for more details.
- *
- * This file was created by members of the firebird development team.
- * All individual contributions remain the Copyright (C) of those
- * individuals. Contributors to this file are either listed here or
- * can be obtained from a source control history command.
- *
- * All rights reserved.
- */
-package org.firebirdsql.jaybird.xca;
-
-import javax.resource.spi.ManagedConnectionMetaData;
-import javax.resource.ResourceException;
-
-import java.sql.SQLException;
-
-/**
- * The class FBManagedConnectionMetaData
implements
- * javax.resource.sqi.ManagedConnectionMetaData
, providing almost
- * no useful information.
- *
- * @author David Jencks
- * @version 1.0
- */
-public class FBManagedConnectionMetaData implements ManagedConnectionMetaData {
-
- private final FBManagedConnection mc;
-
- FBManagedConnectionMetaData(final FBManagedConnection mc)
- {
- this.mc = mc;
- }
-
- /**
- * Returns Product name of the underlying EIS instance connected through
- * the ManagedConnection
.
- *
- * @return Product name of the EIS instance.
- * @throws ResourceException generic exception
- */
- public String getEISProductName() throws ResourceException {
- try {
- return mc.getGDSHelper().getCurrentDatabase().getServerVersion().getServerName();
- } catch(SQLException ex) {
- throw new FBResourceException(ex);
- }
- }
-
- /**
- * Returns product version of the underlying EIS instance connected
- * through the ManagedConnection
.
- *
- * @return Product version of the EIS instance
- * @throws ResourceException generic exception
- */
- public String getEISProductVersion() throws ResourceException {
- try {
- return mc.getGDSHelper().getCurrentDatabase().getServerVersion().getFullVersion();
- } catch(SQLException ex) {
- throw new FBResourceException(ex);
- }
- }
-
- /** Returns maximum limit on number of active concurrent connections that
- * an EIS instance can support across client processes. If an EIS instance
- * does not know about (or does not have) any such limit, it returns a 0.
- *
- * @return Maximum limit for number of active concurrent connections
- * @throws ResourceException generic exception
- */
- public int getMaxConnections() throws ResourceException {
- return 0;
- }
-
- /**
- * Returns name of the user associated with the
- * ManagedConnection
instance. The name corresponds to the
- * resource principal under whose whose security context, a connection to
- * the EIS instance has been established.
- *
- * @return name of the user
- * @throws ResourceException generic exception
- */
- public String getUserName() throws ResourceException {
- try {
- return mc.getGDSHelper().getUserName();
- } catch(SQLException ex) {
- throw new FBResourceException(ex);
- }
- }
- }
diff --git a/src/main/org/firebirdsql/jaybird/xca/FBResourceException.java b/src/main/org/firebirdsql/jaybird/xca/FBResourceException.java
deleted file mode 100644
index 8785eabab5..0000000000
--- a/src/main/org/firebirdsql/jaybird/xca/FBResourceException.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
- *
- * Distributable under LGPL license.
- * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * LGPL License for more details.
- *
- * This file was created by members of the firebird development team.
- * All individual contributions remain the Copyright (C) of those
- * individuals. Contributors to this file are either listed here or
- * can be obtained from a source control history command.
- *
- * All rights reserved.
- */
-package org.firebirdsql.jaybird.xca;
-
-import org.firebirdsql.jdbc.SQLStateConstants;
-
-import javax.resource.ResourceException;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.sql.SQLException;
-
-/**
- * {@code FBResourceException} should be used in places where {@link ResourceException} should be thrown according to
- * the interface specification, but we do not want to lose the exception that we caught.
- *
- * try {
- * // execute some code here
- * ...
- * } catch(GDSException gdsex) {
- * throw new FBResourceException(gdsex);
- * }
- *
- * FBStandAloneConnectionManager
provides the
- * default implementation of ConnectionManager for standalone use.
- * There is no pooling or other features..
+ * The class {@code FBStandAloneConnectionManager} provides the default implementation of FirebirdConnectionManager for
+ * standalone use. There is no pooling or other features.
*
* @author David Jencks
* @author Mark Rotteveel
* @version 1.0
*/
-public class FBStandAloneConnectionManager
- implements ConnectionManager, ConnectionEventListener, Serializable {
-
- private static final long serialVersionUID = -4933951275930670896L;
-
- private transient final static Logger log =
- LoggerFactory.getLogger(FBStandAloneConnectionManager.class);
-
- //package constructor
- FBStandAloneConnectionManager() {
- }
+public class FBStandAloneConnectionManager implements XcaConnectionManager, XcaConnectionEventListener, Serializable {
- //javax.resource.spi.ConnectionManager implementation
+ private static final long serialVersionUID = 1L;
- /**
- * Allocate a new ManagedConnection
.
- *
- * @param mcf The ManagedConnectionFactory
used to create
- * the new connection.
- * @param cxRequestInfo The parameters to be used in creating the
- * new connection
- * @throws ResourceException If the connection cannot be allocated
- */
- public Object allocateConnection(ManagedConnectionFactory mcf,
- ConnectionRequestInfo cxRequestInfo)
- throws ResourceException {
+ private static final Logger log = LoggerFactory.getLogger(FBStandAloneConnectionManager.class);
- FBManagedConnection mc = (FBManagedConnection) mcf.createManagedConnection(null, cxRequestInfo);
- mc.setManagedEnvironment(false);
- mc.setConnectionSharing(false);
- mc.addConnectionEventListener(this);
- return mc.getConnection(null, cxRequestInfo);
+ FBStandAloneConnectionManager() {
}
- //javax.resource.spi.ConnectionEventListener implementation
+ @Override
+ public FirebirdConnection allocateConnection(FBManagedConnectionFactory mcf, FBConnectionRequestInfo cxRequestInfo)
+ throws SQLException {
+ FBManagedConnection mc = mcf.createManagedConnection(cxRequestInfo);
+ mc.setManagedEnvironment(false);
+ mc.addConnectionEventListener(this);
+ return mc.getConnection();
+ }
- /**
- * javax.resource.spi.ConnectionEventListener
callback for
- * when a ManagedConnection
is closed.
- *
- * @param ce contains information about the connection that has be closed
- */
- public void connectionClosed(ConnectionEvent ce) {
+ @Override
+ public void connectionClosed(XcaConnectionEvent ce) {
try {
- ((FBManagedConnection)ce.getSource()).destroy(ce);
- }
- catch (ResourceException e) {
- if (log!=null) log.debug("Exception closing unmanaged connection: ", e);
+ ce.getSource().destroy(ce);
+ } catch (SQLException e) {
+ log.debug("Exception closing unmanaged connection: ", e);
}
}
- /**
- * javax.resource.spi.ConnectionEventListener
callback for
- * when a Local Transaction was rolled back within the context of a
- * ManagedConnection
.
- *
- * @param ce contains information about the connection
- */
- public void connectionErrorOccurred(ConnectionEvent ce) {
- if (log!=null) log.debug("ConnectionErrorOccurred, ", ce.getException());
+ @Override
+ public void connectionErrorOccurred(XcaConnectionEvent ce) {
+ log.debug("ConnectionErrorOccurred, ", ce.getException());
try {
- ((FBManagedConnection)ce.getSource()).destroy(ce);
- }
- catch (ResourceException e) {
- if (log!=null) log.debug("further problems destroying connection: ", e);
+ ce.getSource().destroy(ce);
+ } catch (SQLException e) {
+ log.debug("further problems destroying connection: ", e);
}
}
-
- //We are only supposed to be notified of local transactions that a Connection started.
- //Not much we can do with this info...
-
- /**
- * Ignored event callback
- */
- public void localTransactionStarted(ConnectionEvent event) {}
-
- /**
- * Ignored event callback
- */
- public void localTransactionCommitted(ConnectionEvent event) {}
-
- /**
- * Ignored event callback
- */
- public void localTransactionRolledback(ConnectionEvent event) {}
}
diff --git a/src/main/org/firebirdsql/jaybird/xca/FatalGDSErrorHelper.java b/src/main/org/firebirdsql/jaybird/xca/FatalGDSErrorHelper.java
index fee0131c29..16a84f6d29 100644
--- a/src/main/org/firebirdsql/jaybird/xca/FatalGDSErrorHelper.java
+++ b/src/main/org/firebirdsql/jaybird/xca/FatalGDSErrorHelper.java
@@ -18,76 +18,59 @@
*/
package org.firebirdsql.jaybird.xca;
+import org.firebirdsql.gds.ISCConstants;
+
import java.sql.SQLException;
import java.util.Arrays;
-import org.firebirdsql.gds.GDSException;
-import org.firebirdsql.gds.ISCConstants;
-
/**
- * Helper class for the exception handling in JCA framework. JCA specification
+ * Helper class for the exception handling in XCA framework. JCA specification
* required resource adapter to report an error if it is sure that no other
* operations can be executed over that particular managed connection.
* true
if the exception that happened is fatal
- */
- static boolean isFatal(GDSException ex) {
- int iscErrorCode = ex.getFbErrorCode();
- return Arrays.binarySearch(FATAL_ERRORS, iscErrorCode) >= 0;
- }
-
- /**
- * Check whether the specified exception is fatal from the JCA point of
- * view.
+ * Check whether the specified exception is fatal from the XCA point of view.
*
- * @param ex exception to check.
- *
- * @return true
if the exception that happened is fatal
+ * @param ex
+ * exception to check.
+ * @return {@code true} if the exception that happened is fatal
*/
static boolean isFatal(SQLException ex) {
int errorCode = ex.getErrorCode();
return Arrays.binarySearch(FATAL_ERRORS, errorCode) >= 0;
}
-
+
/**
- * The constant array FATAL_ERRORS
holds an ORDERED
- * list of isc error codes that indicate that the connection is no
- * longer usable. This is used in the jca framework to determine
- * if a GDSException should result in a ConnectionErrorOccurred
- * notification to the Connection Manager to destroy the
- * connection. It is eesntial that this list be ordered so
- * determining if a code is in it can proceed reliably.
- *
- *
+ * The constant array {@code FATAL_ERRORS} holds an ORDERED list of isc error codes that indicate that the
+ * connection is no longer usable. This is used in the XCA framework to determine if a SQLException should result
+ * in a ConnectionErrorOccurred notification to the Connection Manager to destroy the connection. It is essential
+ * that this list be ordered so determining if a code is in it can proceed reliably.
+ * true
if managed connection is participating in
- * transaction.
- *
- * @throws ResourceException if operation cannot be completed.
- */
- boolean inTransaction() throws ResourceException;
-
- /**
- * Get the associated Xid.
- *
- * @return instance of {@link Xid} representing a transaction ID that is
- * managed by this local transaction.
- */
- Xid getXid();
-}
diff --git a/src/main/org/firebirdsql/jaybird/xca/XcaConnectionEvent.java b/src/main/org/firebirdsql/jaybird/xca/XcaConnectionEvent.java
new file mode 100644
index 0000000000..89d63c6be4
--- /dev/null
+++ b/src/main/org/firebirdsql/jaybird/xca/XcaConnectionEvent.java
@@ -0,0 +1,121 @@
+/*
+ * Firebird Open Source JDBC Driver
+ *
+ * Distributable under LGPL license.
+ * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * LGPL License for more details.
+ *
+ * This file was created by members of the firebird development team.
+ * All individual contributions remain the Copyright (C) of those
+ * individuals. Contributors to this file are either listed here or
+ * can be obtained from a source control history command.
+ *
+ * All rights reserved.
+ */
+package org.firebirdsql.jaybird.xca;
+
+import org.firebirdsql.jdbc.FirebirdConnection;
+
+/**
+ * The {@code XcaConnectionEvent} class provides information about the source of a connection related event. A
+ * {@code XcaConnectionEvent} instance contains the following information:
+ *
+ *
+ */
+public class XcaConnectionEvent {
+
+ private final FBManagedConnection source;
+ private final EventType eventType;
+ private final Exception exception;
+ private FirebirdConnection connectionHandle;
+
+ /**
+ * Construct a {@code ConnectionEvent} object.
+ *
+ * @param source
+ * the source of the event
+ * @param eventType
+ * Type of event
+ */
+ public XcaConnectionEvent(FBManagedConnection source, EventType eventType) {
+ this(source, eventType, null);
+ }
+
+ /**
+ * Construct a {@code ConnectionEvent} object.
+ *
+ * @param source
+ * the source of the event
+ * @param eventType
+ * Type of event
+ * @param exception
+ * Exception associated with the event
+ */
+ public XcaConnectionEvent(FBManagedConnection source, EventType eventType, Exception exception) {
+ assert exception != null || eventType != EventType.CONNECTION_ERROR_OCCURRED
+ : "Exception required for CONNECTION_ERROR_OCCURRED";
+ this.source = source;
+ this.eventType = eventType;
+ this.exception = exception;
+ }
+
+ /**
+ * @return The managed connection on which the event initially occurred.
+ */
+ public FBManagedConnection getSource() {
+ return source;
+ }
+
+ /**
+ * Get the connection handle associated with the managed connection instance. Used for {@code CONNECTION_CLOSED}
+ * event.
+ *
+ * @return The connection handle, can be {@code null}
+ */
+ public FirebirdConnection getConnectionHandle() {
+ return connectionHandle;
+ }
+
+ public void setConnectionHandle(FirebirdConnection connectionHandle) {
+ this.connectionHandle = connectionHandle;
+ }
+
+ /**
+ * Get the exception associated with this event.
+ *
+ * @return Exception for this event, can be {@code null} for event type other than {@code CONNECTION_ERROR_OCCURRED}
+ */
+ public Exception getException() {
+ return exception;
+ }
+
+ /**
+ * @return The type of event
+ */
+ public EventType getEventType() {
+ return eventType;
+ }
+
+ public enum EventType {
+ /**
+ * Event notification that an application component has closed the connection.
+ */
+ CONNECTION_CLOSED,
+ /**
+ * Event notification that an error occurred on the connection. This event indicates that the managed
+ * connection instance is now invalid and unusable.
+ */
+ CONNECTION_ERROR_OCCURRED,
+ }
+}
diff --git a/src/main/org/firebirdsql/jaybird/xca/XcaConnectionEventListener.java b/src/main/org/firebirdsql/jaybird/xca/XcaConnectionEventListener.java
new file mode 100644
index 0000000000..f9686979fd
--- /dev/null
+++ b/src/main/org/firebirdsql/jaybird/xca/XcaConnectionEventListener.java
@@ -0,0 +1,58 @@
+/*
+ * Firebird Open Source JDBC Driver
+ *
+ * Distributable under LGPL license.
+ * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * LGPL License for more details.
+ *
+ * This file was created by members of the firebird development team.
+ * All individual contributions remain the Copyright (C) of those
+ * individuals. Contributors to this file are either listed here or
+ * can be obtained from a source control history command.
+ *
+ * All rights reserved.
+ */
+package org.firebirdsql.jaybird.xca;
+
+/**
+ * Listener to receives notification of events on a managed connection instance.
+ */
+public interface XcaConnectionEventListener {
+
+ /**
+ * Notifies the close of a connection.
+ * FBConnection
is a handle to a
@@ -80,9 +74,9 @@ public class FBConnection implements FirebirdConnection, Synchronizable {
// This set contains all allocated but not closed statements
// It is used to close them before the connection is closed
- protected final SetFBDataSource
is a ConnectionFactory for jdbc
- * Connection objects. All work is delegated to a ConnectionManager.
+ * The class {@code FBDataSource} is a ConnectionFactory for jdbc Connection objects. All work is delegated to a
+ * XcaConnectionManager.
+ * Reference
for this DataSource.
- *
- * @param ref
- * The JNDI reference for this DataSource
- */
- @Override
- public void setReference(Reference ref) {
- this.jndiReference = ref;
- }
-
- /**
- * Get the JNDI Reference
for this DataSource.
- *
- * @return The JNDI reference
- */
- @Override
- public Reference getReference() {
- return jndiReference;
- }
-
- @SuppressWarnings("deprecation")
@Override
public Connection getConnection() throws SQLException {
- try {
- return (Connection) cm.allocateConnection(mcf, mcf.getDefaultConnectionRequestInfo());
- } catch (ResourceException re) {
- if (re.getCause() instanceof SQLException) {
- throw (SQLException) re.getCause();
- }
- if (re.getLinkedException() instanceof SQLException) {
- throw (SQLException) re.getLinkedException();
- }
- throw new FBSQLException(re);
- }
+ return cm.allocateConnection(mcf, mcf.getDefaultConnectionRequestInfo());
}
- @SuppressWarnings("deprecation")
@Override
public Connection getConnection(String username, String password) throws SQLException {
- try {
- //mcf makes a copy for us.
- FBConnectionRequestInfo subjectCri = mcf.getDefaultConnectionRequestInfo();
- subjectCri.setUserName(username);
- subjectCri.setPassword(password);
- return (Connection) cm.allocateConnection(mcf, subjectCri);
- } catch (ResourceException re) {
- if (re.getCause() instanceof SQLException) {
- throw (SQLException) re.getCause();
- }
- if (re.getLinkedException() instanceof SQLException) {
- throw (SQLException) re.getLinkedException();
- }
- throw new FBSQLException(re);
- }
+ //mcf makes a copy for us.
+ FBConnectionRequestInfo subjectCri = mcf.getDefaultConnectionRequestInfo();
+ subjectCri.setUserName(username);
+ subjectCri.setPassword(password);
+ return cm.allocateConnection(mcf, subjectCri);
}
@Override
@@ -135,8 +89,9 @@ public boolean isWrapperFor(Class> iface) throws SQLException {
@Override
public
TestFBBlob
here.
- *
- * @author David Jencks
- * @version 1.0
- */
public class TestFBBlob extends TestXABase {
private FBConnection c;
- private LocalTransaction t;
+ private FBLocalTransaction t;
private Exception ex = null;
private int bloblength = 40960 * 10;
protected void setupTable(String name) throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- DataSource ds = (DataSource) mcf.createConnectionFactory();
+ DataSource ds = mcf.createConnectionFactory();
c = (FBConnection) ds.getConnection();
Statement s = c.createStatement();
t = c.getLocalTransaction();
diff --git a/src/test/org/firebirdsql/jaybird/xca/TestFBConnection.java b/src/test/org/firebirdsql/jaybird/xca/TestFBConnection.java
index d04f8e4773..3ee23e6c68 100644
--- a/src/test/org/firebirdsql/jaybird/xca/TestFBConnection.java
+++ b/src/test/org/firebirdsql/jaybird/xca/TestFBConnection.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -20,7 +20,6 @@
import org.junit.Test;
-import javax.resource.spi.ManagedConnection;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.sql.Connection;
@@ -28,43 +27,24 @@
import static org.junit.Assert.assertNotNull;
-/**
- * Describe class TestFBConnection
here.
- *
- * @author David Jencks
- * @version 1.0
- */
public class TestFBConnection extends TestXABase {
@Test
public void testCreateC() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
assertNotNull("Could not get FBManagedConnectionFactory", mcf);
- ManagedConnection mc = mcf.createManagedConnection(null, null);
- assertNotNull("Could not get ManagedConnection", mc);
- Connection c = (Connection) mc.getConnection(null, null);
+ FBManagedConnection mc = mcf.createManagedConnection();
+ assertNotNull("Could not get FBManagedConnection", mc);
+ Connection c = mc.getConnection();
assertNotNull("Could not get Connection", c);
mc.destroy();
}
- @Test
- public void testAssociateC() throws Exception {
- FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc1 = mcf.createManagedConnection(null, null);
- Connection c1 = (Connection) mc1.getConnection(null, null);
- ManagedConnection mc2 = mcf.createManagedConnection(null, null);
- Connection c2 = (Connection) mc2.getConnection(null, null);
- mc1.associateConnection(c2);
- mc2.associateConnection(c1);
- mc1.destroy();
- mc2.destroy();
- }
-
@Test
public void testCreateStatement() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
- Connection c = (Connection) mc.getConnection(null, null);
+ FBManagedConnection mc = mcf.createManagedConnection();
+ Connection c = mc.getConnection();
Statement s = c.createStatement();
assertNotNull("Could not create Statement", s);
mc.destroy();
@@ -73,9 +53,9 @@ public void testCreateStatement() throws Exception {
@Test
public void testUseStatement() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
+ FBManagedConnection mc = mcf.createManagedConnection();
try {
- Connection c = (Connection) mc.getConnection(null, null);
+ Connection c = mc.getConnection();
Statement s = c.createStatement();
XAResource xa = mc.getXAResource();
Exception ex = null;
diff --git a/src/test/org/firebirdsql/jaybird/xca/TestFBManagedConnectionFactory.java b/src/test/org/firebirdsql/jaybird/xca/TestFBManagedConnectionFactory.java
index 2b6821c223..3411584196 100644
--- a/src/test/org/firebirdsql/jaybird/xca/TestFBManagedConnectionFactory.java
+++ b/src/test/org/firebirdsql/jaybird/xca/TestFBManagedConnectionFactory.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -20,21 +20,14 @@
import org.junit.Test;
-import javax.resource.spi.ManagedConnection;
import java.sql.Connection;
import static org.junit.Assert.assertEquals;
-/**
- * Describe class TestFBManagedConnectionFactory
here.
- *
- * @author David Jencks
- * @version 1.0
- */
public class TestFBManagedConnectionFactory extends TestXABase {
@Test
- public void testCreateMcf() throws Exception {
+ public void testCreateMcf() {
// TODO Test doesn't assert anything
initMcf();
}
@@ -43,7 +36,7 @@ public void testCreateMcf() throws Exception {
public void testCreateMc() throws Exception {
// TODO Test doesn't assert anything
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
+ FBManagedConnection mc = mcf.createManagedConnection();
mc.destroy();
}
diff --git a/src/test/org/firebirdsql/jaybird/xca/TestFBResultSet.java b/src/test/org/firebirdsql/jaybird/xca/TestFBResultSet.java
index 605ad978cd..8ac7c7a321 100644
--- a/src/test/org/firebirdsql/jaybird/xca/TestFBResultSet.java
+++ b/src/test/org/firebirdsql/jaybird/xca/TestFBResultSet.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -21,8 +21,6 @@
import org.firebirdsql.jdbc.FBConnection;
import org.junit.Test;
-import javax.resource.spi.LocalTransaction;
-import javax.resource.spi.ManagedConnection;
import javax.sql.DataSource;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
@@ -31,19 +29,13 @@
import static org.firebirdsql.common.DdlHelper.executeCreateTable;
import static org.junit.Assert.*;
-/**
- * Describe class TestFBResultSet
here.
- *
- * @author David Jencks
- * @version 1.0
- */
public class TestFBResultSet extends TestXABase {
@Test
public void testUseResultSet() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
- try (Connection c = (Connection) mc.getConnection(null, null);
+ FBManagedConnection mc = mcf.createManagedConnection();
+ try (Connection c = mc.getConnection();
Statement s = c.createStatement()) {
XAResource xa = mc.getXAResource();
Exception ex = null;
@@ -86,8 +78,8 @@ public void testUseResultSet() throws Exception {
@Test
public void testUseResultSetMore() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
- try (Connection c = (Connection) mc.getConnection(null, null);
+ FBManagedConnection mc = mcf.createManagedConnection();
+ try (Connection c = mc.getConnection();
Statement s = c.createStatement()) {
XAResource xa = mc.getXAResource();
Exception ex = null;
@@ -148,10 +140,10 @@ public void testUseResultSetMore() throws Exception {
@Test
public void testUseResultSetWithPreparedStatement() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- DataSource ds = (DataSource) mcf.createConnectionFactory();
+ DataSource ds = mcf.createConnectionFactory();
try (FBConnection c = (FBConnection) ds.getConnection();
Statement s = c.createStatement()) {
- LocalTransaction t = c.getLocalTransaction();
+ FBLocalTransaction t = c.getLocalTransaction();
Exception ex = null;
t.begin();
try {
@@ -238,10 +230,10 @@ public void testUseResultSetWithPreparedStatement() throws Exception {
@Test
public void testUsePreparedStatementAcrossTransactions() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- DataSource ds = (DataSource) mcf.createConnectionFactory();
+ DataSource ds = mcf.createConnectionFactory();
try (FBConnection c = (FBConnection) ds.getConnection();
Statement s = c.createStatement()) {
- LocalTransaction t = c.getLocalTransaction();
+ FBLocalTransaction t = c.getLocalTransaction();
t.begin();
executeCreateTable(c, "DROP TABLE T1");
t.commit();
@@ -334,10 +326,10 @@ public void testUsePreparedStatementAcrossTransactions() throws Exception {
@Test
public void testUseResultSetWithCount() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- DataSource ds = (DataSource) mcf.createConnectionFactory();
+ DataSource ds = mcf.createConnectionFactory();
try (FBConnection c = (FBConnection) ds.getConnection();
Statement s = c.createStatement()) {
- LocalTransaction t = c.getLocalTransaction();
+ FBLocalTransaction t = c.getLocalTransaction();
Exception ex = null;
t.begin();
try {
@@ -375,10 +367,10 @@ public void testUseResultSetWithCount() throws Exception {
@Test
public void testExecutableProcedure() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- DataSource ds = (DataSource) mcf.createConnectionFactory();
+ DataSource ds = mcf.createConnectionFactory();
try (FBConnection c = (FBConnection) ds.getConnection();
Statement s = c.createStatement()) {
- LocalTransaction t = c.getLocalTransaction();
+ FBLocalTransaction t = c.getLocalTransaction();
t.begin();
executeCreateTable(c, "DROP PROCEDURE testproc");
t.commit();
diff --git a/src/test/org/firebirdsql/jaybird/xca/TestFBStandAloneConnectionManager.java b/src/test/org/firebirdsql/jaybird/xca/TestFBStandAloneConnectionManager.java
index 615bf15b10..6f163ced0f 100644
--- a/src/test/org/firebirdsql/jaybird/xca/TestFBStandAloneConnectionManager.java
+++ b/src/test/org/firebirdsql/jaybird/xca/TestFBStandAloneConnectionManager.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -38,7 +38,7 @@ public class TestFBStandAloneConnectionManager extends TestXABase {
@Test
public void testCreateDCM() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- DataSource ds = (DataSource) mcf.createConnectionFactory();
+ DataSource ds = mcf.createConnectionFactory();
assertNotNull("Could not get DataSource", ds);
Connection c = ds.getConnection();
assertNotNull("Could not get Connection", c);
@@ -48,7 +48,7 @@ public void testCreateDCM() throws Exception {
@Test
public void testCreateStatement() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- DataSource ds = (DataSource) mcf.createConnectionFactory();
+ DataSource ds = mcf.createConnectionFactory();
Connection c = ds.getConnection();
Statement s = c.createStatement();
assertNotNull("Could not get Statement", s);
@@ -58,10 +58,10 @@ public void testCreateStatement() throws Exception {
@Test
public void testUseStatement() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- DataSource ds = (DataSource) mcf.createConnectionFactory();
+ DataSource ds = mcf.createConnectionFactory();
FBConnection c = (FBConnection) ds.getConnection();
Statement s = c.createStatement();
- FirebirdLocalTransaction t = c.getLocalTransaction();
+ FBLocalTransaction t = c.getLocalTransaction();
assertNotNull("Could not get LocalTransaction", t);
Exception ex = null;
t.begin();
diff --git a/src/test/org/firebirdsql/jaybird/xca/TestFBXAResource.java b/src/test/org/firebirdsql/jaybird/xca/TestFBXAResource.java
index e492b2a3e5..5b4db7a52a 100644
--- a/src/test/org/firebirdsql/jaybird/xca/TestFBXAResource.java
+++ b/src/test/org/firebirdsql/jaybird/xca/TestFBXAResource.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -20,7 +20,6 @@
import org.junit.Test;
-import javax.resource.spi.ManagedConnection;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.sql.Connection;
@@ -31,18 +30,12 @@
import static org.firebirdsql.common.FBTestProperties.getConnectionViaDriverManager;
import static org.junit.Assert.*;
-/**
- * Describe class TestFBXAResource
here.
- *
- * @author David Jencks
- * @version 1.0
- */
public class TestFBXAResource extends TestXABase {
@Test
public void testGetXAResource() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
+ FBManagedConnection mc = mcf.createManagedConnection();
try {
XAResource xa1 = mc.getXAResource();
XAResource xa2 = mc.getXAResource();
@@ -55,12 +48,12 @@ public void testGetXAResource() throws Exception {
@Test
public void testIsSameRM() throws Exception {
FBManagedConnectionFactory mcf1 = initMcf();
- ManagedConnection mc1 = mcf1.createManagedConnection(null, null);
+ FBManagedConnection mc1 = mcf1.createManagedConnection();
XAResource xa1 = mc1.getXAResource();
- ManagedConnection mc2 = mcf1.createManagedConnection(null, null);
+ FBManagedConnection mc2 = mcf1.createManagedConnection();
XAResource xa2 = mc2.getXAResource();
FBManagedConnectionFactory mcf3 = initMcf();
- ManagedConnection mc3 = mcf3.createManagedConnection(null, null);
+ FBManagedConnection mc3 = mcf3.createManagedConnection();
XAResource xa3 = mc3.getXAResource();
if (xa1.isSameRM(xa2)) {
fail("isSameRM reports no difference from same mcf");
@@ -76,12 +69,11 @@ public void testIsSameRM() throws Exception {
@Test
public void testStartXATrans() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
- FBManagedConnection fbmc = (FBManagedConnection) mc;
+ FBManagedConnection mc = mcf.createManagedConnection();
XAResource xa = mc.getXAResource();
Xid xid = new XidImpl();
xa.start(xid, XAResource.TMNOFLAGS);
- assertNotNull("no db handle after start xid", fbmc.getGDSHelper().getCurrentDatabase());
+ assertNotNull("no db handle after start xid", mc.getGDSHelper().getCurrentDatabase());
xa.end(xid, XAResource.TMSUCCESS);
xa.commit(xid, true);
mc.destroy();
@@ -90,12 +82,11 @@ public void testStartXATrans() throws Exception {
@Test
public void testRollbackXATrans() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
- FBManagedConnection fbmc = (FBManagedConnection) mc;
+ FBManagedConnection mc = mcf.createManagedConnection();
XAResource xa = mc.getXAResource();
Xid xid = new XidImpl();
xa.start(xid, XAResource.TMNOFLAGS);
- assertNotNull("no db handle after start xid", fbmc.getGDSHelper().getCurrentDatabase());
+ assertNotNull("no db handle after start xid", mc.getGDSHelper().getCurrentDatabase());
xa.end(xid, XAResource.TMSUCCESS);
xa.rollback(xid);
mc.destroy();
@@ -104,12 +95,11 @@ public void testRollbackXATrans() throws Exception {
@Test
public void test2PCXATrans() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
- FBManagedConnection fbmc = (FBManagedConnection) mc;
+ FBManagedConnection mc = mcf.createManagedConnection();
XAResource xa = mc.getXAResource();
Xid xid = new XidImpl();
xa.start(xid, XAResource.TMNOFLAGS);
- assertNotNull("no db handle after start xid", fbmc.getGDSHelper().getCurrentDatabase());
+ assertNotNull("no db handle after start xid", mc.getGDSHelper().getCurrentDatabase());
xa.end(xid, XAResource.TMSUCCESS);
xa.prepare(xid);
xa.commit(xid, false);
@@ -119,12 +109,11 @@ public void test2PCXATrans() throws Exception {
@Test
public void testRollback2PCXATrans() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc = mcf.createManagedConnection(null, null);
- FBManagedConnection fbmc = (FBManagedConnection) mc;
+ FBManagedConnection mc = mcf.createManagedConnection();
XAResource xa = mc.getXAResource();
Xid xid = new XidImpl();
xa.start(xid, XAResource.TMNOFLAGS);
- assertNotNull("no db handle after start xid", fbmc.getGDSHelper().getCurrentDatabase());
+ assertNotNull("no db handle after start xid", mc.getGDSHelper().getCurrentDatabase());
xa.end(xid, XAResource.TMSUCCESS);
xa.prepare(xid);
xa.rollback(xid);
@@ -134,18 +123,16 @@ public void testRollback2PCXATrans() throws Exception {
@Test
public void testDo2XATrans() throws Exception {
FBManagedConnectionFactory mcf = initMcf();
- ManagedConnection mc1 = mcf.createManagedConnection(null, null);
- FBManagedConnection fbmc1 = (FBManagedConnection) mc1;
+ FBManagedConnection mc1 = mcf.createManagedConnection();
XAResource xa1 = mc1.getXAResource();
Xid xid1 = new XidImpl();
xa1.start(xid1, XAResource.TMNOFLAGS);
- assertNotNull("no db handle after start xid", fbmc1.getGDSHelper().getCurrentDatabase());
- ManagedConnection mc2 = mcf.createManagedConnection(null, null);
- FBManagedConnection fbmc2 = (FBManagedConnection) mc2;
+ assertNotNull("no db handle after start xid", mc1.getGDSHelper().getCurrentDatabase());
+ FBManagedConnection mc2 = mcf.createManagedConnection();
XAResource xa2 = mc2.getXAResource();
Xid xid2 = new XidImpl();
xa2.start(xid2, XAResource.TMNOFLAGS);
- assertNotNull("no db handle after start xid", fbmc2.getGDSHelper().getCurrentDatabase());
+ assertNotNull("no db handle after start xid", mc2.getGDSHelper().getCurrentDatabase());
//commit each tr on other xares
xa1.end(xid1, XAResource.TMSUCCESS);
xa2.commit(xid1, true);
@@ -172,14 +159,13 @@ public void testRecover() throws Exception {
Xid xid1 = new XidImpl();
- ManagedConnection mc1 = mcf.createManagedConnection(null, null);
+ FBManagedConnection mc1 = mcf.createManagedConnection();
try {
- FBManagedConnection fbmc1 = (FBManagedConnection) mc1;
XAResource xa1 = mc1.getXAResource();
xa1.start(xid1, XAResource.TMNOFLAGS);
- Connection fbc1 = (Connection) fbmc1.getConnection(null, null);
+ Connection fbc1 = mc1.getConnection();
try (Statement fbstmt1 = fbc1.createStatement()) {
fbstmt1.execute("INSERT INTO test_reconnect(id) VALUES(1)");
}
@@ -193,7 +179,7 @@ public void testRecover() throws Exception {
FBManagedConnectionFactory mcf2 = initMcf();
- ManagedConnection mc2 = mcf2.createManagedConnection(null, null);
+ FBManagedConnection mc2 = mcf2.createManagedConnection();
try {
XAResource xa2 = mc2.getXAResource();
@@ -228,7 +214,7 @@ public void testRecover() throws Exception {
ResultSet rs = stmt.executeQuery("SELECT * FROM test_reconnect");
assertTrue("Should find at least one row.", rs.next());
assertEquals("Should read correct value", 1, rs.getInt(1));
- assertTrue("Should select only one row", !rs.next());
+ assertFalse("Should select only one row", rs.next());
}
}
@@ -241,12 +227,10 @@ public void testRecover() throws Exception {
@Test
public void testXAMultipleStatements() throws Throwable {
FBManagedConnectionFactory mcf = initMcf();
- FBManagedConnection mc = (FBManagedConnection) mcf.createManagedConnection(null, null);
- // TODO Test fails with connectionSharing enabled, as that doesn't reset the managedEnvironment status currently used to fix the issue
- mc.setConnectionSharing(false);
+ FBManagedConnection mc = mcf.createManagedConnection();
try {
XAResource xa = mc.getXAResource();
- Connection con = (Connection) mc.getConnection(null, null);
+ Connection con = mc.getConnection();
Xid xid = new XidImpl();
xa.start(xid, XAResource.TMNOFLAGS);
@@ -283,12 +267,10 @@ public void testXAMultipleStatements() throws Throwable {
@Test
public void testCloseConnectionDuringXA() throws Throwable {
FBManagedConnectionFactory mcf = initMcf();
- FBManagedConnection mc = (FBManagedConnection) mcf.createManagedConnection(null, null);
- // TODO Original issue could not be reproduced with connectionSharing=true
- mc.setConnectionSharing(false);
+ FBManagedConnection mc = mcf.createManagedConnection();
try {
XAResource xa = mc.getXAResource();
- Connection con = (Connection) mc.getConnection(null, null);
+ Connection con = mc.getConnection();
Xid xid = new XidImpl();
xa.start(xid, XAResource.TMNOFLAGS);
diff --git a/src/test/org/firebirdsql/jaybird/xca/TestXABase.java b/src/test/org/firebirdsql/jaybird/xca/TestXABase.java
index 124a8942b1..fb3367a44a 100644
--- a/src/test/org/firebirdsql/jaybird/xca/TestXABase.java
+++ b/src/test/org/firebirdsql/jaybird/xca/TestXABase.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -24,6 +24,7 @@
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.concurrent.atomic.AtomicInteger;
import static org.firebirdsql.common.FBTestProperties.*;
@@ -31,15 +32,12 @@
* THIS FILE INCLUDES AN XID IMPLEMENTATION FROM THE JBOSS PROJECT
* www.jboss.org.
*
- * Describe class TestXABase
here.
- *
* @author David Jencks
- * @version 1.0
*/
public abstract class TestXABase extends FBJUnit4TestBase {
public FBManagedConnectionFactory initMcf() {
- FBManagedConnectionFactory mcf = createFBManagedConnectionFactory(new InternalConnectionManager());
+ FBManagedConnectionFactory mcf = createFBManagedConnectionFactory();
mcf.setDatabase(DB_DATASOURCE_URL);
mcf.setUserName(DB_USER);
mcf.setPassword(DB_PASSWORD);
@@ -62,15 +60,13 @@ public FBManagedConnectionFactory initMcf() {
* This object encapsulates the ID of a transaction.
* This implementation is immutable and always serializable at runtime.
*
- * @author Rickard �berg (rickard.oberg@telkel.com)
+ * @author Rickard Oberg (rickard.oberg@telkel.com)
* @author Ole Husgaard
- * @version $Revision$
*/
- public static class XidImpl
- implements Xid, Serializable {
- // Constants -----------------------------------------------------
+ public static class XidImpl implements Xid, Serializable {
public static final int JBOSS_FORMAT_ID = 0x0101;
+ private static final long serialVersionUID = 1L;
// Attributes ----------------------------------------------------
@@ -81,9 +77,11 @@ public static class XidImpl
/**
* Global transaction id of this instance.
+ * * The coding of this class depends on the fact that this variable is * initialized in the constructor and never modified. References to * this array are never given away, instead a clone is delivered. + *
*/ private byte[] globalId; @@ -93,48 +91,49 @@ public static class XidImpl */ private byte[] branchId; - // Static -------------------------------------------------------- - /** * The host name of this host, followed by a slash. - * + ** This is used for building globally unique transaction identifiers. * It would be safer to use the IP address, but a host name is better * for humans to read and will do for now. + *
*/ - private static String hostName; + private static final String HOST_NAME; /** * The next transaction id to use on this host. */ - static private int nextId = 0; + private static final AtomicInteger nextId = new AtomicInteger(); /** * Return a new unique transaction id to use on this host. */ - static private synchronized int getNextId() { - return nextId++; + private static synchronized int getNextId() { + return nextId.getAndIncrement(); } /** * Singleton for no branch qualifier. */ - static private byte[] noBranchQualifier = new byte[0]; + private static final byte[] noBranchQualifier = new byte[0]; /* * Initialize thehostName
class variable.
*/
static {
+ String tempHostName;
try {
- hostName = InetAddress.getLocalHost().getHostName() + "/";
- // Ensure room for 14 digits of serial no.
- if (hostName.length() > MAXGTRIDSIZE - 15) {
- hostName = hostName.substring(0, MAXGTRIDSIZE - 15);
- hostName = hostName + "/";
- }
+ tempHostName = InetAddress.getLocalHost().getHostName() + "/";
} catch (UnknownHostException e) {
- hostName = "localhost/";
+ tempHostName = "localhost/";
+ }
+ // Ensure room for 14 digits of serial no.
+ if (tempHostName.length() > MAXGTRIDSIZE - 15) {
+ tempHostName = tempHostName.substring(0, MAXGTRIDSIZE - 15);
+ tempHostName = tempHostName + "/";
}
+ HOST_NAME = tempHostName;
}
/**
@@ -160,28 +159,10 @@ static String toString(Xid id) {
*/
public XidImpl() {
hash = getNextId();
- globalId = (hostName + Integer.toString(hash)).getBytes();
+ globalId = (HOST_NAME + hash).getBytes();
branchId = noBranchQualifier;
}
- /**
- * Create a new branch of an existing global transaction ID.
- *
- * @param xid
- * The transaction ID to create a new branch of.
- * @param branchId
- * The ID of the new branch.
- */
- public XidImpl(XidImpl xid, int branchId) {
- this.hash = xid.hash;
- this.globalId = xid.globalId; // reuse array instance, we never modify.
- this.branchId = Integer.toString(branchId).getBytes();
- }
-
- // Public --------------------------------------------------------
-
- // Xid implementation --------------------------------------------
-
/**
* Return the global transaction id of this transaction.
*/
@@ -193,10 +174,11 @@ public byte[] getGlobalTransactionId() {
* Return the branch qualifier of this transaction.
*/
public byte[] getBranchQualifier() {
- if (branchId.length == 0)
+ if (branchId.length == 0) {
return branchId; // Zero length arrays are immutable.
- else
+ } else {
return branchId.clone();
+ }
}
/**
@@ -232,17 +214,21 @@ public boolean equals(Object obj) {
if (obj instanceof XidImpl) {
XidImpl other = (XidImpl) obj;
- if (globalId.length != other.globalId.length ||
- branchId.length != other.branchId.length)
+ if (globalId.length != other.globalId.length || branchId.length != other.branchId.length) {
return false;
+ }
- for (int i = 0; i < globalId.length; ++i)
- if (globalId[i] != other.globalId[i])
+ for (int i = 0; i < globalId.length; ++i) {
+ if (globalId[i] != other.globalId[i]) {
return false;
+ }
+ }
- for (int i = 0; i < branchId.length; ++i)
- if (branchId[i] != other.branchId[i])
+ for (int i = 0; i < branchId.length; ++i) {
+ if (branchId[i] != other.branchId[i]) {
return false;
+ }
+ }
return true;
}
@@ -256,17 +242,5 @@ public int hashCode() {
public String toString() {
return toString(this);
}
-
- /**
- * Return the global transaction id of this transaction.
- * Unlike the {@link #getGlobalTransactionId()} method, this one
- * returns a reference to the global id byte array that may not
- * be changed.
- */
- public byte[] getInternalGlobalTransactionId() {
- return globalId.clone();
- }
-
}
-
}
diff --git a/src/test/org/firebirdsql/jdbc/TestFBTpbMapper.java b/src/test/org/firebirdsql/jdbc/TestFBTpbMapper.java
index 5b8b6c8a5d..5bfc4a37f9 100644
--- a/src/test/org/firebirdsql/jdbc/TestFBTpbMapper.java
+++ b/src/test/org/firebirdsql/jdbc/TestFBTpbMapper.java
@@ -1,5 +1,5 @@
/*
- * Firebird Open Source JavaEE Connector - JDBC Driver
+ * Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
@@ -20,12 +20,12 @@
import org.firebirdsql.gds.ISCConstants;
import org.firebirdsql.gds.TransactionParameterBuffer;
-import org.firebirdsql.jaybird.xca.FBResourceException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.sql.Connection;
+import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@@ -76,8 +76,8 @@ public void testNewWithMappingFile() throws Exception {
}
@Test
- public void testNewMappingFileDoesNotExist_throwsFBResourceException() throws Exception {
- expectedException.expect(FBResourceException.class);
+ public void testNewMappingFileDoesNotExist_throwsSQLException() throws Exception {
+ expectedException.expect(SQLException.class);
new FBTpbMapper(TEST_TPB_MAPPING + "does_not_exist", getClass().getClassLoader());
}
@@ -110,10 +110,10 @@ public void testNewWithIncompleteMap_unspecifiedUseDefaults() throws Exception {
}
@Test
- public void testNewMapContainsInvalidName_throwsFBResourceException() throws Exception {
+ public void testNewMapContainsInvalidName_throwsSQLException() throws Exception {
final Map