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 @@ - diff --git a/build.xml b/build.xml index 9dc6dc8cc7..00f0577b7b 100644 --- a/build.xml +++ b/build.xml @@ -1,13 +1,12 @@ - + - diff --git a/build/archive.xml b/build/archive.xml index 9a3dd17225..b551f81869 100644 --- a/build/archive.xml +++ b/build/archive.xml @@ -3,15 +3,9 @@ - + - - - - - - - + @@ -19,7 +13,6 @@ - @@ -30,24 +23,6 @@ - - - - - - - - - - - - - - - - - - @@ -101,18 +76,4 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/clean.xml b/build/clean.xml deleted file mode 100644 index 93c557212e..0000000000 --- a/build/clean.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/build/compile.xml b/build/compile.xml index 5c38a2a21b..24a2b96dcf 100644 --- a/build/compile.xml +++ b/build/compile.xml @@ -4,13 +4,7 @@ - - - - - - + depends="compile-driver, compile-tests, compile-etc, compile-resources, compile-stylesheets"/> diff --git a/build/deploy.xml b/build/deploy.xml deleted file mode 100644 index 407d1ce3f1..0000000000 --- a/build/deploy.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/dist.xml b/build/dist.xml index 3bcb5bb801..d0eb0ffada 100644 --- a/build/dist.xml +++ b/build/dist.xml @@ -12,7 +12,7 @@ doctitle="${Name}" additionalparam="${javadoc.additionalparam}" excludepackagenames="${excludedPackages}" - bottom="Copyright &copy; 2001-${YEAR} Jaybird (Firebird JDBC/JCA) team. All rights reserved."> + bottom="Copyright &copy; 2001-${YEAR} Jaybird (Firebird JDBC) team. All rights reserved."> @@ -60,7 +60,6 @@ - @@ -71,9 +70,6 @@ - - - diff --git a/build/init.xml b/build/init.xml index 738eb9e134..3ae20ab0cc 100644 --- a/build/init.xml +++ b/build/init.xml @@ -67,8 +67,6 @@ - - @@ -92,12 +90,6 @@ - - - @@ -122,11 +114,7 @@ - - - - @@ -200,13 +188,7 @@ --> - - - - - - diff --git a/build/maven-release/jaybird-relocation.pom b/build/maven-release/jaybird-relocation.pom index 434237efd2..f13d8e9cac 100644 --- a/build/maven-release/jaybird-relocation.pom +++ b/build/maven-release/jaybird-relocation.pom @@ -30,7 +30,7 @@ https://www.gnu.org/copyleft/lesser.html repo - The Jaybird JCA/JDBC driver is distributed free of charge under the + The Jaybird JDBC driver is distributed free of charge under the GNU Lesser General Public License (LGPL). Text of the license can be obtained from http://www.gnu.org/copyleft/lesser.html diff --git a/build/maven-release/jaybird-template.pom b/build/maven-release/jaybird-template.pom index 9e89b8fec2..3f60ac8ad8 100644 --- a/build/maven-release/jaybird-template.pom +++ b/build/maven-release/jaybird-template.pom @@ -1,3 +1,4 @@ + https://www.gnu.org/copyleft/lesser.html repo - The Jaybird JCA/JDBC driver is distributed free of charge under the + The Jaybird JDBC driver is distributed free of charge under the GNU Lesser General Public License (LGPL). Text of the license can be obtained from https://www.gnu.org/copyleft/lesser.html @@ -174,11 +175,6 @@ - - javax.resource - connector-api - 1.5 - org.antlr antlr4-runtime diff --git a/build/test.xml b/build/test.xml index 0651f9b9f4..c05583cf03 100644 --- a/build/test.xml +++ b/build/test.xml @@ -36,7 +36,6 @@ - @@ -103,7 +102,6 @@ - @@ -173,7 +171,6 @@ - diff --git a/build/user.properties b/build/user.properties index a372294bc3..e69de29bb2 100644 --- a/build/user.properties +++ b/build/user.properties @@ -1,2 +0,0 @@ -jboss.base.dir = /usr/java/jboss/rc1/jboss-all/build/output/jboss-3.0.1RC1 -jboss.deploy.dir = ${jboss.base.dir}/server/all/deploy diff --git a/build/util.xml b/build/util.xml index 0723e2d409..82b5767f0e 100644 --- a/build/util.xml +++ b/build/util.xml @@ -19,13 +19,4 @@ - - - - - - - - - \ No newline at end of file diff --git a/devdoc/jdp/jdp-2020-03-remove-jca-support.md b/devdoc/jdp/jdp-2020-03-remove-jca-support.md index 125b315d1d..109d8390f6 100644 --- a/devdoc/jdp/jdp-2020-03-remove-jca-support.md +++ b/devdoc/jdp/jdp-2020-03-remove-jca-support.md @@ -2,8 +2,8 @@ ## Status -- Draft -- Proposed for: Jaybird 5 +- Published +- Implemented in: Jaybird 5 ## Type diff --git a/src/documentation/faq.md b/src/documentation/faq.md index e9370b8b48..2c074dba77 100644 --- a/src/documentation/faq.md +++ b/src/documentation/faq.md @@ -6,6 +6,41 @@ Where do I get Jaybird? ### Maven ### + + #### Jaybird 4 #### Jaybird 4 is available from Maven central: @@ -172,8 +207,8 @@ Each release is also tagged in the repository. How is Jaybird licensed? ------------------------ -Jaybird JCA/JDBC driver is distributed under the GNU Lesser General Public -License (LGPL). Text of the license can be obtained from +Jaybird JDBC driver is distributed under the GNU Lesser General Public License +(LGPL). Text of the license can be obtained from [http://www.gnu.org/copyleft/lesser.html](http://www.gnu.org/copyleft/lesser.html). Using Jaybird (by importing Jaybird's public interfaces in your Java code), and @@ -690,15 +725,11 @@ implemented as they are not supported by Firebird. Implemented features: * Most useful JDBC functionality ("useful" in the opinion of the developers). -* Complete JCA API support: may be used directly in JCA-supporting application - servers. -* XA transactions with true two phase commit when used as a JCA resource adapter - in a managed environment (with a `TransactionManager` and JCA deployment - support) as well as when used via `javax.sql.XADataSource` implementation. +* XA transactions with true two phase commit when used via + `javax.sql.XADataSource` implementation. * `ObjectFactory` implementation for use in environments with JNDI but no `TransactionManager`. * `DataSource` implementations without pooling. -* Driver implementation for use in legacy applications. * Complete access to all Firebird database parameter block and transaction parameter block settings. * JMX mbean for database management (so far just database create and drop). diff --git a/src/documentation/release_notes.md b/src/documentation/release_notes.md index f9131ab519..2503fb0629 100644 --- a/src/documentation/release_notes.md +++ b/src/documentation/release_notes.md @@ -112,30 +112,6 @@ For example: ~~~ -If your application is deployed to a Java EE application server, you will need to -exclude the `javax.resource:connector-api` dependency, and add it as a provided -dependency: - -~~~ {.xml} - - org.firebirdsql.jdbc - jaybird - @VERSION_EXAMPLE@ - - - javax.resource - connector-api - - - - - javax.resource - connector-api - 1.5 - provided - -~~~ - If you want to use Type 2 support (native, local or embedded), you need to explicitly include JNA 5.5.0 as a dependency: @@ -281,6 +257,37 @@ Support for Java 7 dropped Jaybird 5 does not support Java 7. You will need to upgrade to Java 8 or higher, or remain on Jaybird 4. +Removal of JavaEE/JakartaEE Connector Architecture (JCA) +-------------------------------------------------------- + +The JavaEE/JakartaEE Connector Architecture (JCA) implementation that was the +core of Jaybird has been removed. The package `org.firebirdsql.jca` no longer +exists, and it is no longer possible to use Jaybird as a JCA connector (Resource +Adapter). + +From its inception, Jaybird has been built around the - then new - JCA +specification. Unfortunately, this had the side-effect that Jaybird required the +JCA api (`connector-api`) as a dependency. As far as we know, Jaybird was hardly +used as a JCA connector, while at the same time it hindered development, as the +JCA implementation was central to Jaybird. Lack of testing as a JCA connector +also meant it was unclear if Jaybird actually functioned correctly as such. + +To reduce development overhead, we have decided to remove support for JCA from +Jaybird. A lot of classes previously in the `org.firebirdsql.jca` package are +now in the package `org.firebirdsql.jaybird.xca`. This new package is marked as +internal API and is not binary compatible with the old JCA implementation. Be +aware that the API and implementation of the classes in this package can change +in any point release. + +If there turns out to be actual demand for JCA support in Jaybird after all, we +will consider creating new support for JCA in a way that does not require JCA +when using Jaybird as a JDBC driver. Contact us on the firebird-java mailing +list if you're interested in such a solution. + +As a result of this change, `org.firebirdsql.jdbc.FBDataSource` is now +considered internal API as well. For normal data sources, look at the classes in +the package `org.firebirdsql.ds`. + Removal of classes, packages and methods without deprecation ------------------------------------------------------------ diff --git a/src/lib/connector-api-1.5.jar b/src/lib/connector-api-1.5.jar deleted file mode 100644 index 683762e1f51e3ccef2fd98335fd281a7fad79dd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36479 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 diff --git a/src/main/org/firebirdsql/ds/DataSourceFactory.java b/src/main/org/firebirdsql/ds/DataSourceFactory.java index dc15e8303e..d86303d421 100644 --- a/src/main/org/firebirdsql/ds/DataSourceFactory.java +++ b/src/main/org/firebirdsql/ds/DataSourceFactory.java @@ -1,7 +1,5 @@ /* - * $Id$ - * - * Firebird Open Source J2EE 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 @@ -14,64 +12,79 @@ * 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 CVS history command. + * can be obtained from a source control history command. * * All rights reserved. */ package org.firebirdsql.ds; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Hashtable; +import org.firebirdsql.jaybird.xca.FBManagedConnectionFactory; +import org.firebirdsql.jdbc.FBConnectionProperties; +import org.firebirdsql.logging.LoggerFactory; import javax.naming.Context; import javax.naming.Name; import javax.naming.RefAddr; import javax.naming.Reference; import javax.naming.spi.ObjectFactory; - -import org.firebirdsql.jdbc.FBConnectionProperties; +import java.io.*; +import java.util.Hashtable; /** * ObjectFactory for the DataSources in org.firebirdsql.ds. - * + * * @author Mark Rotteveel * @since 2.2 */ public class DataSourceFactory implements ObjectFactory { + @Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { - Reference ref = (Reference)obj; + Reference ref = (Reference) obj; String className = ref.getClassName(); - if (className.equals("org.firebirdsql.ds.FBConnectionPoolDataSource")) { + switch (className) { + case "org.firebirdsql.ds.FBConnectionPoolDataSource": return loadConnectionPoolDS(ref); - } - if (className.equals("org.firebirdsql.ds.FBXADataSource")) { + case "org.firebirdsql.ds.FBXADataSource": return loadXADS(ref); + case "org.firebirdsql.ds.FBSimpleDataSource": + return loadSimpleDS(ref); + default: + return null; } - - return null; } private Object loadConnectionPoolDS(Reference ref) throws Exception { FBConnectionPoolDataSource ds = new FBConnectionPoolDataSource(); loadAbstractCommonDataSource(ds, ref); - + return ds; } - + private Object loadXADS(Reference ref) throws Exception { FBXADataSource ds = new FBXADataSource(); loadAbstractCommonDataSource(ds, ref); - + + return ds; + } + + private Object loadSimpleDS(Reference ref) { + RefAddr propertyContent = ref.get(FBSimpleDataSource.REF_MCF); + FBManagedConnectionFactory mcf = null; + if (propertyContent != null) { + byte[] data = (byte[]) propertyContent.getContent(); + mcf = (FBManagedConnectionFactory) deserialize(data); + } + if (mcf == null) { + mcf = new FBManagedConnectionFactory(); + } + FBSimpleDataSource ds = new FBSimpleDataSource(mcf); + ds.setDescription(getRefAddr(ref, FBSimpleDataSource.REF_DESCRIPTION)); return ds; } - + private void loadAbstractCommonDataSource(FBAbstractCommonDataSource ds, Reference ref) throws Exception { RefAddr propertyContent = ref.get(FBAbstractCommonDataSource.REF_PROPERTIES); if (propertyContent != null) { @@ -93,46 +106,46 @@ private void loadAbstractCommonDataSource(FBAbstractCommonDataSource ds, Referen */ ds.getConnectionProperties().setDatabase(oldDatabase); } - + /** * Retrieves the content of the given Reference address (type). - * - * @param ref Reference - * @param type Address or type + * + * @param ref + * Reference + * @param type + * Address or type * @return Content as String */ protected static String getRefAddr(Reference ref, String type) { RefAddr addr = ref.get(type); if (addr == null) { return null; - } + } Object content = addr.getContent(); return content != null ? content.toString() : null; } - + protected static byte[] serialize(Object obj) { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - try { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); out.writeObject(obj); out.flush(); - } catch(IOException ex) { + return bout.toByteArray(); + } catch (IOException e) { + LoggerFactory.getLogger(DataSourceFactory.class).warn("Could not serialize object, returning null", e); return null; } - - return bout.toByteArray(); } protected static Object deserialize(byte[] data) { ByteArrayInputStream bin = new ByteArrayInputStream(data); - + try { ObjectInputStream in = new ObjectInputStream(bin); return in.readObject(); - } catch(IOException ex) { - return null; - } catch(ClassNotFoundException ex) { + } catch (IOException | ClassNotFoundException ex) { + LoggerFactory.getLogger(DataSourceFactory.class).warn("Could not deserialize object, returning null", ex); return null; } } diff --git a/src/main/org/firebirdsql/ds/FBAbstractCommonDataSource.java b/src/main/org/firebirdsql/ds/FBAbstractCommonDataSource.java index 86c1307e4b..5a8fe5c25a 100644 --- a/src/main/org/firebirdsql/ds/FBAbstractCommonDataSource.java +++ b/src/main/org/firebirdsql/ds/FBAbstractCommonDataSource.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 @@ -687,10 +687,11 @@ protected final void setDatabase() { } protected final void setConnectionProperties(FBConnectionProperties connectionProperties) { + if (connectionProperties == null) { + throw new NullPointerException("null value not allowed for connectionProperties"); + } synchronized (lock) { - if (connectionProperties == null) { - throw new NullPointerException("null value not allowed for connectionProperties"); - } + checkNotStarted(); this.connectionProperties = connectionProperties; } } diff --git a/src/main/org/firebirdsql/ds/FBConnectionPoolDataSource.java b/src/main/org/firebirdsql/ds/FBConnectionPoolDataSource.java index 3e50bd9432..831ff7b621 100644 --- a/src/main/org/firebirdsql/ds/FBConnectionPoolDataSource.java +++ b/src/main/org/firebirdsql/ds/FBConnectionPoolDataSource.java @@ -1,7 +1,5 @@ /* - * $Id$ - * - * Firebird Open Source J2EE 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 @@ -14,26 +12,23 @@ * 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 CVS history command. + * can be obtained from a source control history command. * * All rights reserved. */ package org.firebirdsql.ds; -import java.sql.SQLException; +import org.firebirdsql.gds.impl.GDSFactory; +import org.firebirdsql.gds.impl.GDSType; +import org.firebirdsql.jaybird.xca.FBManagedConnectionFactory; +import org.firebirdsql.jdbc.FBDataSource; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; -import javax.resource.ResourceException; import javax.sql.ConnectionPoolDataSource; import javax.sql.PooledConnection; - -import org.firebirdsql.gds.impl.GDSFactory; -import org.firebirdsql.gds.impl.GDSType; -import org.firebirdsql.jaybird.xca.FBManagedConnectionFactory; -import org.firebirdsql.jdbc.FBDataSource; -import org.firebirdsql.jdbc.FBSQLException; +import java.sql.SQLException; /** * Bare-bones implementation of {@link javax.sql.ConnectionPoolDataSource}. @@ -41,15 +36,18 @@ * Please be aware that this is not a connectionpool. This class provides * PooledConnection objects for connection pool implementations (eg as provided * by a JEE application server). If you need a standalone connectionpool, - * consider using a connectionpool implementation like c3p0, BoneCP or DBCP. + * consider using a connectionpool implementation like HikariCP, c3p0 or DBCP. *

* * @author Mark Rotteveel * @since 2.2 */ -public class FBConnectionPoolDataSource extends FBAbstractCommonDataSource implements ConnectionPoolDataSource, Referenceable { +public class FBConnectionPoolDataSource extends FBAbstractCommonDataSource implements ConnectionPoolDataSource, + Referenceable { + + // TODO Implement in terms of FBManagedConnectionFactory - private volatile transient FBDataSource internalDs; + private volatile FBDataSource internalDs; public PooledConnection getPooledConnection() throws SQLException { return getPooledConnection(getUser(), getPassword()); @@ -67,18 +65,13 @@ private void initialize() throws SQLException { if (internalDs != null) { return; } - try { - GDSType gdsType = GDSType.getType(getType()); - if (gdsType == null) { - gdsType = GDSFactory.getDefaultGDSType(); - } - FBManagedConnectionFactory mcf = new FBManagedConnectionFactory( - gdsType, getConnectionProperties()); - internalDs = (FBDataSource) mcf.createConnectionFactory(); - internalDs.setLogWriter(getLogWriter()); - } catch (ResourceException e) { - throw new FBSQLException(e); + GDSType gdsType = GDSType.getType(getType()); + if (gdsType == null) { + gdsType = GDSFactory.getDefaultGDSType(); } + FBManagedConnectionFactory mcf = new FBManagedConnectionFactory(gdsType, getConnectionProperties()); + internalDs = (FBDataSource) mcf.createConnectionFactory(); + internalDs.setLogWriter(getLogWriter()); } } @@ -89,7 +82,8 @@ protected void checkNotStarted() { } public Reference getReference() throws NamingException { - Reference ref = new Reference(getClass().getName(), DataSourceFactory.class.getName(), null); + Reference ref = new Reference(FBConnectionPoolDataSource.class.getName(), + DataSourceFactory.class.getName(), null); FBAbstractCommonDataSource.updateReference(ref, this); diff --git a/src/main/org/firebirdsql/ds/FBSimpleDataSource.java b/src/main/org/firebirdsql/ds/FBSimpleDataSource.java index d6becaf9a0..7ed04f92ae 100644 --- a/src/main/org/firebirdsql/ds/FBSimpleDataSource.java +++ b/src/main/org/firebirdsql/ds/FBSimpleDataSource.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 @@ -26,10 +26,7 @@ import org.firebirdsql.jdbc.FBDataSource; import org.firebirdsql.jdbc.FirebirdConnectionProperties; -import javax.naming.NamingException; -import javax.naming.Reference; -import javax.resource.Referenceable; -import javax.resource.ResourceException; +import javax.naming.*; import javax.sql.DataSource; import java.io.Serializable; import java.sql.Connection; @@ -39,60 +36,79 @@ * This is a simple implementation of {@link DataSource} interface. Connections * are physically opened in {@link DataSource#getConnection()} method and * physically closed in {@link Connection#close()} method. - * + *

+ * If you need a standalone connection pool, consider using a connection pool implementation like HikariCP, c3p0 or + * DBCP. + *

+ * * @author Roman Rokytskyy * @author David Jencks - * @version 1.0 */ -public class FBSimpleDataSource extends RootCommonDataSource implements DataSource, Serializable, Referenceable, FirebirdConnectionProperties { - +public class FBSimpleDataSource extends RootCommonDataSource implements DataSource, Serializable, Referenceable, + FirebirdConnectionProperties { + private static final long serialVersionUID = 3156578540634970427L; - - protected FBManagedConnectionFactory mcf; + static final String REF_DESCRIPTION = "description"; + static final String REF_MCF = "mcf"; + + protected final FBManagedConnectionFactory mcf; protected transient FBDataSource ds; - - protected Reference jndiReference; + protected String description; /** - * Create instance of this class. + * Creates an instance using the default GDS type (PURE_JAVA). */ public FBSimpleDataSource() { this(GDSFactory.getDefaultGDSType()); } /** - * Create instance of this class. + * Creates an instance using the specified GDS type. + * + * @param type + * GDS type */ public FBSimpleDataSource(GDSType type) { mcf = new FBManagedConnectionFactory(type); } - + /** - * Get buffer length for the BLOB fields. - * + * Creates an instance using an existing FBManagedConnectionFactory. + * + * @param mcf + * Managed connection factory + * @see DataSourceFactory + */ + FBSimpleDataSource(FBManagedConnectionFactory mcf) { + this.mcf = mcf; + } + + /** + * Get buffer length for the BLOB fields. + * * @return length of BLOB buffer. */ public Integer getBlobBufferLength() { return mcf.getBlobBufferSize(); } - + /** - * Set BLOB buffer length. This value influences the performance when + * Set BLOB buffer length. This value influences the performance when * working with BLOB fields. - * - * @param length new length of the BLOB buffer. + * + * @param length + * new length of the BLOB buffer. */ public void setBlobBufferLength(Integer length) { mcf.setBlobBufferSize(length); } - + /** - * Get name of the database. - * + * Get name of the database. + * * @return database name, value is equal to the part of full JDBC URL without * the {@code jdbc:firebirdsql:} part. - * * @deprecated use {@link #getDatabase} instead for the sake of naming * compatibility. */ @@ -100,15 +116,15 @@ public void setBlobBufferLength(Integer length) { public String getDatabaseName() { return getDatabase(); } - + /** * Set database name. - * - * @param name connection URL without {@code "jdbc:firebirdsql:"} - * prefix ({@code "//localhost:3050/c:/database/employee.fdb"}) for - * example). - * - * @deprecated use {@link #setDatabase(String)} instead for the sake of + * + * @param name + * connection URL without {@code "jdbc:firebirdsql:"} + * prefix ({@code "//localhost:3050/c:/database/employee.fdb"}) for + * example). + * @deprecated use {@link #setDatabase(String)} instead for the sake of * naming compatibility. */ @Deprecated @@ -117,8 +133,8 @@ public void setDatabaseName(String name) { } /** - * Get name of the database. - * + * Get name of the database. + * * @return database name, value is equal to the part of full JDBC URL without * the {@code jdbc:firebirdsql:} part. */ @@ -128,20 +144,20 @@ public String getDatabase() { /** * Set database name. - * - * @param name connection URL without {@code "jdbc:firebirdsql:"} - * prefix ({@code "//localhost:3050/c:/database/employee.fdb"}) for - * example). + * + * @param name + * connection URL without {@code "jdbc:firebirdsql:"} + * prefix ({@code "//localhost:3050/c:/database/employee.fdb"}) for + * example). */ public void setDatabase(String name) { mcf.setDatabase(name); } - + /** * Get user name that is used in {@link #getConnection()} method. - * + * * @return default user name. - * * @deprecated use {@link #getUserName()} instead for the sake of naming * compatibility. */ @@ -149,84 +165,85 @@ public void setDatabase(String name) { public String getUser() { return getUserName(); } - + /** * Set user name that will be used in {@link #getConnection()} method. - * - * @param user default user name. - * - * @deprecated use {@link #setUserName(String)} instead for the sake of - * naming compatibility. + * + * @param user + * default user name. + * @deprecated use {@link #setUserName(String)} instead for the sake of naming compatibility. */ @Deprecated public void setUser(String user) { setUserName(user); } - + /** * Get user name that is used in {@link #getConnection()} method. - * + * * @return default user name. */ public String getUserName() { return mcf.getUserName(); } - + /** * Set user name that will be used in {@link #getConnection()} method. - * - * @param userName default user name. + * + * @param userName + * default user name. */ public void setUserName(String userName) { mcf.setUserName(userName); } - + /** * Get password used in {@link #getConnection()} method. - * - * @return password corresponding to the user name returned by + * + * @return password corresponding to the user name returned by * {@link #getUserName()}. */ public String getPassword() { return mcf.getPassword(); } - + /** * Set password that will be used in the {@link #getConnection()} method. - * - * @param password password corresponding to the user name set in - * {@link #setUserName(String)}. + * + * @param password + * password corresponding to the user name set in {@link #setUserName(String)}. */ public void setPassword(String password) { mcf.setPassword(password); } - + /** * Get encoding for connections produced by this data source. - * + * * @return encoding for the connection. */ public String getEncoding() { return mcf.getEncoding(); } - + /** * Set encoding for connections produced by this data source. - * - * @param encoding encoding for the connection. + * + * @param encoding + * encoding for the connection. */ public void setEncoding(String encoding) { mcf.setEncoding(encoding); } - + public String getTpbMapping() { return mcf.getTpbMapping(); } - + public void setTpbMapping(String tpbMapping) { mcf.setTpbMapping(tpbMapping); } - + public int getBlobBufferSize() { return mcf.getBlobBufferSize(); } @@ -345,8 +362,8 @@ public boolean isDefaultResultSetHoldable() { public void setDefaultResultSetHoldable(boolean isHoldable) { mcf.setDefaultResultSetHoldable(isHoldable); - } - + } + public int getSoTimeout() { return mcf.getSoTimeout(); } @@ -354,11 +371,11 @@ public int getSoTimeout() { public void setSoTimeout(int soTimeout) { mcf.setSoTimeout(soTimeout); } - + public int getConnectTimeout() { return mcf.getConnectTimeout(); } - + public void setConnectTimeout(int connectTimeout) { mcf.setConnectTimeout(connectTimeout); } @@ -453,36 +470,21 @@ public void setWireCompression(boolean wireCompression) { mcf.setWireCompression(wireCompression); } - /* - * INTERFACES IMPLEMENTATION - */ - - /** - * Get previously set JNDI reference. - * - * @return instance of {@link Reference} set previously. - * - * @throws NamingException if something went wrong. - */ + @Override public Reference getReference() throws NamingException { - return jndiReference; - } - - /** - * Set JNDI reference for this data source. - * - * @param reference reference to set. - */ - public void setReference(Reference reference) { - jndiReference = reference; + Reference ref = new Reference(FBSimpleDataSource.class.getName(), DataSourceFactory.class.getName(), null); + ref.add(new StringRefAddr(REF_DESCRIPTION, getDescription())); + byte[] data = DataSourceFactory.serialize(mcf); + ref.add(new BinaryRefAddr(REF_MCF, data)); + return ref; } /** * Get JDBC connection with default credentials. - * + * * @return new JDBC connection. - * - * @throws SQLException if something went wrong. + * @throws SQLException + * if something went wrong. */ public Connection getConnection() throws SQLException { return getDataSource().getConnection(); @@ -490,13 +492,14 @@ public Connection getConnection() throws SQLException { /** * Get JDBC connection with the specified credentials. - * - * @param username user name for the connection. - * @param password password for the connection. - * + * + * @param username + * user name for the connection. + * @param password + * password for the connection. * @return new JDBC connection. - * - * @throws SQLException if something went wrong. + * @throws SQLException + * if something went wrong. */ public Connection getConnection(String username, String password) throws SQLException { return getDataSource().getConnection(username, password); @@ -521,62 +524,59 @@ public int getLoginTimeout() throws SQLException { public void setLoginTimeout(int loginTimeout) throws SQLException { setConnectTimeout(loginTimeout); } - + /** * Get description of this datasource. - * + * * @return description of this datasource. */ public String getDescription() { return description; } - + /** * Set description of this datasource. - * - * @param description description of this datasource. + * + * @param description + * description of this datasource. */ public void setDescription(String description) { this.description = description; } - + /** - * Get underlying connection factory (in our case instance of - * {@link FBDataSource} class) that will provide JDBC connections. - * + * Get underlying connection factory (in our case instance of {@link FBDataSource} class) that will provide JDBC + * connections. + * * @return JDBC connection factory. - * - * @throws SQLException if something went wrong. + * @throws SQLException + * if something went wrong. */ protected synchronized DataSource getDataSource() throws SQLException { - if (ds != null) + if (ds != null) { return ds; - - if (mcf.getDatabase() == null || "".equals(mcf.getDatabase().trim())) - throw new SQLException( - "Database was not specified. Cannot provide connections."); - - try { - ds = (FBDataSource)mcf.createConnectionFactory(); - - return ds; - } catch(ResourceException rex) { - - throw new SQLException(rex.getMessage()); - } + + if (mcf.getDatabase() == null || "".equals(mcf.getDatabase().trim())) { + throw new SQLException("Database was not specified. Cannot provide connections."); + } + + ds = (FBDataSource) mcf.createConnectionFactory(); + + return ds; } - + // JDBC 4.0 - + public boolean isWrapperFor(Class iface) throws SQLException { return iface != null && iface.isAssignableFrom(getClass()); } public T unwrap(Class iface) throws SQLException { - if (!isWrapperFor(iface)) + if (!isWrapperFor(iface)) { throw new SQLException("Unable to unwrap to class " + iface.getName()); - + } + return iface.cast(this); } } diff --git a/src/main/org/firebirdsql/ds/FBXADataSource.java b/src/main/org/firebirdsql/ds/FBXADataSource.java index cae638abe5..d2e55d5ba0 100644 --- a/src/main/org/firebirdsql/ds/FBXADataSource.java +++ b/src/main/org/firebirdsql/ds/FBXADataSource.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,38 +18,36 @@ */ package org.firebirdsql.ds; -import java.io.PrintWriter; -import java.sql.SQLException; +import org.firebirdsql.gds.impl.GDSFactory; +import org.firebirdsql.gds.impl.GDSType; +import org.firebirdsql.jaybird.xca.*; +import org.firebirdsql.jdbc.FBConnection; +import org.firebirdsql.jdbc.FBDataSource; +import org.firebirdsql.jdbc.FirebirdConnection; +import org.firebirdsql.logging.Logger; +import org.firebirdsql.logging.LoggerFactory; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; -import javax.resource.ResourceException; -import javax.resource.spi.ConnectionEvent; -import javax.resource.spi.ConnectionEventListener; -import javax.resource.spi.ConnectionManager; -import javax.resource.spi.ConnectionRequestInfo; -import javax.resource.spi.ManagedConnectionFactory; import javax.sql.XAConnection; import javax.sql.XADataSource; - -import org.firebirdsql.gds.impl.GDSFactory; -import org.firebirdsql.gds.impl.GDSType; -import org.firebirdsql.jaybird.xca.FBManagedConnection; -import org.firebirdsql.jaybird.xca.FBManagedConnectionFactory; -import org.firebirdsql.jdbc.FBConnection; -import org.firebirdsql.jdbc.FBDataSource; -import org.firebirdsql.jdbc.FBSQLException; +import java.io.Serializable; +import java.sql.SQLException; /** * Bare-bones implementation of {@link javax.sql.XADataSource}. - * + * * @author Mark Rotteveel * @since 2.2 */ public class FBXADataSource extends FBAbstractCommonDataSource implements XADataSource, Referenceable { - private volatile transient FBDataSource internalDs; + // TODO Implement in terms of FBManagedConnectionFactory + + private static final Logger LOG = LoggerFactory.getLogger(FBXADataSource.class); + + private volatile FBDataSource internalDs; public XAConnection getXAConnection() throws SQLException { return getXAConnection(getUser(), getPassword()); @@ -59,8 +57,7 @@ public XAConnection getXAConnection(String user, String password) throws SQLExce if (internalDs == null) { initialize(); } - FBConnection connection = (FBConnection) internalDs.getConnection(user, - password); + FBConnection connection = (FBConnection) internalDs.getConnection(user, password); return new FBXAConnection(connection); } @@ -69,19 +66,15 @@ private void initialize() throws SQLException { if (internalDs != null) { return; } - try { - GDSType gdsType = GDSType.getType(getType()); - if (gdsType == null) { - gdsType = GDSFactory.getDefaultGDSType(); - } - FBManagedConnectionFactory mcf = new FBManagedConnectionFactory(gdsType, - getConnectionProperties()); - mcf.setDefaultConnectionManager(new XAConnectionManager()); - internalDs = (FBDataSource) mcf.createConnectionFactory(); - internalDs.setLogWriter(getLogWriter()); - } catch (ResourceException e) { - throw new FBSQLException(e); + GDSType gdsType = GDSType.getType(getType()); + if (gdsType == null) { + gdsType = GDSFactory.getDefaultGDSType(); } + FBManagedConnectionFactory mcf = new FBManagedConnectionFactory(gdsType, + getConnectionProperties()); + mcf.setDefaultConnectionManager(new XAConnectionManager()); + internalDs = (FBDataSource) mcf.createConnectionFactory(); + internalDs.setLogWriter(getLogWriter()); } } @@ -91,83 +84,41 @@ protected void checkNotStarted() throws IllegalStateException { } } - private static class XAConnectionManager implements ConnectionManager, ConnectionEventListener { + private static class XAConnectionManager implements XcaConnectionManager, XcaConnectionEventListener, + Serializable { - private static final long serialVersionUID = 2615167799315401379L; + private static final long serialVersionUID = 7926533334548378200L; - public Object allocateConnection(ManagedConnectionFactory mcf, - ConnectionRequestInfo cxRequestInfo) throws ResourceException { - - FBManagedConnection mc = (FBManagedConnection) mcf.createManagedConnection(null, cxRequestInfo); + @Override + public FirebirdConnection allocateConnection(FBManagedConnectionFactory mcf, + FBConnectionRequestInfo cxRequestInfo) throws SQLException { + FBManagedConnection mc = mcf.createManagedConnection(cxRequestInfo); mc.setManagedEnvironment(true); - mc.setConnectionSharing(false); mc.addConnectionEventListener(this); - return mc.getConnection(null, cxRequestInfo); + return mc.getConnection(); } - // javax.resource.spi.ConnectionEventListener implementation - - /** - * 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. *

- * 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. *

diff --git a/src/main/org/firebirdsql/jaybird/xca/FBConnectionRequestInfo.java b/src/main/org/firebirdsql/jaybird/xca/FBConnectionRequestInfo.java index e20083430e..4b42dea893 100644 --- a/src/main/org/firebirdsql/jaybird/xca/FBConnectionRequestInfo.java +++ b/src/main/org/firebirdsql/jaybird/xca/FBConnectionRequestInfo.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 @@ -25,25 +25,23 @@ import org.firebirdsql.gds.impl.DatabaseParameterBufferExtension; import org.firebirdsql.gds.impl.wire.Xdrable; -import javax.resource.cci.ConnectionSpec; -import javax.resource.spi.ConnectionRequestInfo; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.util.Iterator; /** - * The class 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 List connectionEventListeners = new CopyOnWriteArrayList<>(); - private final List connectionHandles = Collections.synchronizedList(new ArrayList()); + private final List connectionEventListeners = new CopyOnWriteArrayList<>(); + private static final AtomicReferenceFieldUpdater connectionHandleUpdater = + AtomicReferenceFieldUpdater.newUpdater(FBManagedConnection.class, FBConnection.class, "connectionHandle"); + private volatile FBConnection connectionHandle; // This is a bit of hack to be able to get attach warnings into the FBConnection that is created later. - private SQLWarning unnotifiedWarnings; + private static final AtomicReferenceFieldUpdater unnotifiedWarningsUpdater = + AtomicReferenceFieldUpdater.newUpdater(FBManagedConnection.class, SQLWarning.class, "unnotifiedWarnings"); + private volatile SQLWarning unnotifiedWarnings; private int timeout = 0; @@ -87,125 +86,98 @@ public class FBManagedConnection implements ManagedConnection, XAResource, Excep private GDSHelper gdsHelper; private final FbDatabase database; private final Object syncObject; - + private XAResource xaResource; private final FBConnectionRequestInfo cri; private FBTpb tpb; private int transactionIsolation; private volatile boolean managedEnvironment = true; - private volatile boolean connectionSharing = true; - private final Set preparedXid = Collections.synchronizedSet(new HashSet()); + private final Set preparedXid = Collections.synchronizedSet(new HashSet<>()); private volatile boolean inDistributedTransaction = false; - FBManagedConnection(Subject subject, ConnectionRequestInfo cri, FBManagedConnectionFactory mcf) - throws ResourceException { + FBManagedConnection(FBConnectionRequestInfo cri, FBManagedConnectionFactory mcf) throws SQLException { this.mcf = mcf; - this.cri = getCombinedConnectionRequestInfo(subject, cri); + this.cri = getCombinedConnectionRequestInfo(cri); this.tpb = mcf.getDefaultTpb(); this.transactionIsolation = mcf.getDefaultTransactionIsolation(); //TODO: XIDs in limbo should be loaded so that XAER_DUPID can be thrown appropriately - try { - DatabaseParameterBuffer dpb = this.cri.getDpb(); - - if (dpb.getArgumentAsString(DatabaseParameterBuffer.LC_CTYPE) == null - && dpb.getArgumentAsString(DatabaseParameterBufferExtension.LOCAL_ENCODING) == null) { - String defaultEncoding = getDefaultConnectionEncoding(); - if (defaultEncoding == null) { - throw new SQLNonTransientConnectionException(ERROR_NO_CHARSET, - SQLStateConstants.SQL_STATE_CONNECTION_ERROR) ; - } - dpb.addArgument(DatabaseParameterBuffer.LC_CTYPE, defaultEncoding); + DatabaseParameterBuffer dpb = this.cri.getDpb(); - String warningMessage = WARNING_NO_CHARSET + defaultEncoding; - log.warn(warningMessage); - notifyWarning(new SQLWarning(warningMessage)); + if (dpb.getArgumentAsString(DatabaseParameterBuffer.LC_CTYPE) == null + && dpb.getArgumentAsString(DatabaseParameterBufferExtension.LOCAL_ENCODING) == null) { + String defaultEncoding = getDefaultConnectionEncoding(); + if (defaultEncoding == null) { + throw new SQLNonTransientConnectionException(ERROR_NO_CHARSET, + SQLStateConstants.SQL_STATE_CONNECTION_ERROR); } + dpb.addArgument(DatabaseParameterBuffer.LC_CTYPE, defaultEncoding); - if (!dpb.hasArgument(DatabaseParameterBuffer.CONNECT_TIMEOUT) && DriverManager.getLoginTimeout() > 0) { - dpb.addArgument(DatabaseParameterBuffer.CONNECT_TIMEOUT, DriverManager.getLoginTimeout()); - } - - final FbConnectionProperties connectionProperties = new FbConnectionProperties(); - connectionProperties.fromDpb(dpb); - // TODO Move this logic to the GDSType or database factory? - final String gdsTypeName = mcf.getGDSType().toString(); - if (!(EmbeddedGDSFactoryPlugin.EMBEDDED_TYPE_NAME.equals(gdsTypeName) - || LocalGDSFactoryPlugin.LOCAL_TYPE_NAME.equals(gdsTypeName))) { - final DbAttachInfo dbAttachInfo = DbAttachInfo.parseConnectString(mcf.getDatabase()); - connectionProperties.setServerName(dbAttachInfo.getServer()); - connectionProperties.setPortNumber(dbAttachInfo.getPort()); - connectionProperties.setDatabaseName(dbAttachInfo.getFileName()); - } else { - connectionProperties.setDatabaseName(mcf.getDatabase()); - } + String warningMessage = WARNING_NO_CHARSET + defaultEncoding; + log.warn(warningMessage); + notifyWarning(new SQLWarning(warningMessage)); + } - database = mcf.getDatabaseFactory().connect(connectionProperties); - database.addDatabaseListener(new MCDatabaseListener()); - database.addExceptionListener(this); - database.attach(); - syncObject = database.getSynchronizationObject(); + if (!dpb.hasArgument(DatabaseParameterBuffer.CONNECT_TIMEOUT) && DriverManager.getLoginTimeout() > 0) { + dpb.addArgument(DatabaseParameterBuffer.CONNECT_TIMEOUT, DriverManager.getLoginTimeout()); + } - gdsHelper = new GDSHelper(database); - } catch(SQLException ex) { - throw new FBResourceException(ex); + final FbConnectionProperties connectionProperties = new FbConnectionProperties(); + connectionProperties.fromDpb(dpb); + // TODO Move this logic to the GDSType or database factory? + final String gdsTypeName = mcf.getGDSType().toString(); + if (!(EmbeddedGDSFactoryPlugin.EMBEDDED_TYPE_NAME.equals(gdsTypeName) + || LocalGDSFactoryPlugin.LOCAL_TYPE_NAME.equals(gdsTypeName))) { + final DbAttachInfo dbAttachInfo = DbAttachInfo.parseConnectString(mcf.getDatabase()); + connectionProperties.setServerName(dbAttachInfo.getServer()); + connectionProperties.setPortNumber(dbAttachInfo.getPort()); + connectionProperties.setDatabaseName(dbAttachInfo.getFileName()); + } else { + connectionProperties.setDatabaseName(mcf.getDatabase()); } + + database = mcf.getDatabaseFactory().connect(connectionProperties); + database.addDatabaseListener(new MCDatabaseListener()); + database.addExceptionListener(this); + database.attach(); + syncObject = database.getSynchronizationObject(); + + gdsHelper = new GDSHelper(database); } @Override public void errorOccurred(Object source, SQLException ex) { log.trace(ex.getMessage()); - if (!FatalGDSErrorHelper.isFatal(ex)) + if (!FatalGDSErrorHelper.isFatal(ex)) { return; - - ConnectionEvent event = new ConnectionEvent(FBManagedConnection.this, ConnectionEvent.CONNECTION_ERROR_OCCURRED, + } + XcaConnectionEvent event = new XcaConnectionEvent(this, XcaConnectionEvent.EventType.CONNECTION_ERROR_OCCURRED, ex); - FBManagedConnection.this.notify(connectionErrorOccurredNotifier, event); + notify(connectionErrorOccurredNotifier, event); } - private FBConnectionRequestInfo getCombinedConnectionRequestInfo(Subject subject, ConnectionRequestInfo cri) - throws ResourceException { + private FBConnectionRequestInfo getCombinedConnectionRequestInfo(FBConnectionRequestInfo cri) throws SQLException { if (cri == null) { - cri = mcf.getDefaultConnectionRequestInfo(); - } - try { - FBConnectionRequestInfo fbcri = (FBConnectionRequestInfo) cri; - if (subject != null) { - // see connector spec, section 8.2.6, contract for - // ManagedConnectionFactory, option A. - for (Object cred : subject.getPrivateCredentials()) { - if (cred instanceof PasswordCredential - && mcf.equals(((PasswordCredential) cred) - .getManagedConnectionFactory())) { - PasswordCredential pcred = (PasswordCredential) cred; - String user = pcred.getUserName(); - String password = new String(pcred.getPassword()); - fbcri.setPassword(password); - fbcri.setUserName(user); - break; - } - } - } - - return fbcri; - } catch (ClassCastException cce) { - throw new FBResourceException("Incorrect ConnectionRequestInfo class supplied"); + return mcf.getDefaultConnectionRequestInfo(); } + return cri; } /** * Get instance of {@link GDSHelper} connected with this managed connection. * * @return instance of {@link GDSHelper}. - * @throws SQLException If this connection has no GDSHelper + * @throws SQLException + * If this connection has no GDSHelper */ public GDSHelper getGDSHelper() throws SQLException { - if (gdsHelper == null) + if (gdsHelper == null) { // TODO Right error code? throw new FbExceptionBuilder().exception(ISCConstants.isc_req_no_trans).toSQLException(); + } return gdsHelper; } @@ -222,386 +194,189 @@ public boolean inTransaction() { return gdsHelper != null && gdsHelper.inTransaction(); } - public void setManagedEnvironment(boolean managedEnvironment) throws ResourceException{ + public void setManagedEnvironment(boolean managedEnvironment) throws SQLException { this.managedEnvironment = managedEnvironment; - - // if connection sharing is not enabled, notify currently associated - // connection handle about the state change. - if (!connectionSharing) { - synchronized (connectionHandles) { - if (connectionHandles.size() > 1) - throw new javax.resource.spi.IllegalStateException( - "Multiple connections associated with this managed " + - "connection in non-sharing mode."); - - // there will be at most one connection. - for (FBConnection connection : connectionHandles) { - try { - connection.setManagedEnvironment(managedEnvironment); - } catch (SQLException ex) { - throw new FBResourceException(ex); - } - } - } + final FBConnection connection = connectionHandle; + if (connection != null) { + connection.setManagedEnvironment(managedEnvironment); } } /** - * Check if connection sharing is enabled. When connection sharing is - * enabled, multiple connection handles ({@link FBConnection} instances) - * can access this managed connection in thread-safe manner (they synchronize - * on this instance). This feature can be enabled only in JCA environment, - * any other environment must not use connection sharing. - * - * @return true 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. + *

+ * The FBLocalTransaction is used by the container to manage local * transactions for a RM instance. + *

* - * @return LocalTransaction instance - * @throws ResourceException - * generic exception if operation fails - * @throws javax.resource.NotSupportedException - * if the operation is not supported - * @throws ResourceAdapterInternalException - * resource adapter internal error condition - */ - public LocalTransaction getLocalTransaction() { - return new FBLocalTransaction(this, null); - } - - /** - * Gets the metadata information for this connection's underlying EIS - * resource manager instance. The ManagedConnectionMetaData interface - * provides information about the underlying EIS instance associated with - * the ManagedConenction instance. - * - * @return ManagedConnectionMetaData instance - * @throws ResourceException - * generic exception if operation fails - * @throws javax.resource.NotSupportedException - * if the operation is not supported + * @return FBLocalTransaction instance */ - public ManagedConnectionMetaData getMetaData() throws ResourceException { - return new FBManagedConnectionMetaData(this); + public FBLocalTransaction getLocalTransaction() { + return new FBLocalTransaction(this); } /** - * Sets the log writer for this ManagedConnection 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 - * 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. - *

- * The log writer is a character output stream to which all logging and - * tracing messages for this ManagedConnection instance will be printed. - * ConnectionManager manages the association of output stream - * with the ManagedConnection instance based on the - * connection pooling requirements. - *

- * The Log writer associated with a 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. - *

- * 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. + *

* - * @param connection - * Application-level connection handle - * @throws ResourceException - * Failed to associate the connection handle with this - * ManagedConnection instance - * @throws javax.resource.spi.IllegalStateException - * Illegal state for invoking this method - * @throws ResourceAdapterInternalException - * Resource adapter internal error condition + * @throws SQLException + * generic exception if operation fails */ - public void associateConnection(Object connection) throws ResourceException { - if (!connectionSharing) + // TODO Consider removing (though might be used to implement XADataSource/ConnectionPoolDataSource without proxies) + public void cleanup() throws SQLException { + synchronized (syncObject) { disassociateConnections(); - try { - final FBConnection abstractConnection = (FBConnection) connection; - abstractConnection.setManagedConnection(this); - connectionHandles.add(abstractConnection); - } catch (ClassCastException cce) { - throw new FBResourceException("invalid connection supplied to associateConnection.", cce); - } - } - - /** - * Application server calls this method to force any cleanup on the - * ManagedConnection instance. - *

- * 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 ManagedConnection instance. Any attempt - * by an application component to use the connection handle after cleanup of - * the underlying ManagedConnection should result in an - * exception. - *

- * 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 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 { - SQLExceptionChainBuilder chain = new SQLExceptionChainBuilder<>(); - - synchronized (connectionHandles) { - // Iterate over copy of list as connection.close() will remove connection - List connectionHandleCopy = new ArrayList<>(connectionHandles); - for (FBConnection connection : connectionHandleCopy) { - try { - connection.close(); - } catch (SQLException sqlex) { - chain.append(sqlex); - } - } + private void disassociateConnections() throws SQLException { + final FBConnection connection = connectionHandle; + if (connection != null) { + connection.close(); } - - if (chain.hasException()) - throw new FBResourceException(chain.getException()); } /** * Disassociate connections without cleanly closing them. */ private void forceDisassociateConnections() { - synchronized (connectionHandles) { - Iterator connectionIterator = connectionHandles.iterator(); - while (connectionIterator.hasNext()) { - FBConnection connection = connectionIterator.next(); + final FBConnection connection = connectionHandleUpdater.getAndSet(this, null); + if (connection != null) { + try { connection.setManagedConnection(null); - try { - connection.close(); - } catch (SQLException sqlex) { - log.debug("Exception ignored during forced disassociation", sqlex); - } - connectionIterator.remove(); + connection.close(); + } catch (SQLException sqlex) { + log.debug("Exception ignored during forced disassociation", sqlex); } } } /** - * Creates a new connection handle for the underlying physical connection - * represented by the ManagedConnection 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. - *

- * - * The 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. + *

+ * 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. + *

* - * @throws ResourceException - * generic exception if operation failed - * @throws javax.resource.spi.IllegalStateException - * illegal state for destroying connection + * @throws SQLException + * generic exception if operation failed */ - public void destroy() throws ResourceException { + public void destroy() throws SQLException { destroy(null); } - public void destroy(ConnectionEvent connectionEvent) throws ResourceException { - if (gdsHelper == null) + public void destroy(XcaConnectionEvent connectionEvent) throws SQLException { + if (gdsHelper == null) { return; + } try { if (isBrokenConnection(connectionEvent)) { FbDatabase currentDatabase = gdsHelper.getCurrentDatabase(); currentDatabase.forceClose(); } else { - if (inTransaction()) - throw new javax.resource.spi.IllegalStateException( - "Can't destroy managed connection with active transaction"); + if (inTransaction()) { + // TODO More specific exception, Jaybird error code + // TODO should we skip disassociation in this case? + throw new SQLException("Can't destroy managed connection with active transaction"); + } gdsHelper.detachDatabase(); } - } catch (SQLException ge) { - throw new FBResourceException("Can't detach from db.", ge); } finally { gdsHelper = null; forceDisassociateConnections(); } } - private boolean isBrokenConnection(ConnectionEvent connectionEvent) { - if (connectionEvent == null || connectionEvent.getId() != ConnectionEvent.CONNECTION_ERROR_OCCURRED) { + private boolean isBrokenConnection(XcaConnectionEvent connectionEvent) { + if (connectionEvent == null + || connectionEvent.getEventType() != XcaConnectionEvent.EventType.CONNECTION_ERROR_OCCURRED) { return false; } @@ -619,6 +394,8 @@ private boolean isBrokenConnection(ConnectionEvent connectionEvent) { return true; } + // TODO Should this test for SocketException or something else, as SocketTimeoutException is also tested in the + // previous check //noinspection RedundantIfStatement if (findException(connectionEventException, SocketTimeoutException.class) != null) { return true; @@ -645,20 +422,25 @@ private T findException(Exception root, Class exception } /** - * Return an XA resource to the caller. - *

- * In both 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. + *

+ * To support xa semantics, the correct db handle must be located whenever a managed connection is associated with a + * xid. + *

+ * * @author David Jencks * @author Mark Rotteveel */ -public class FBManagedConnectionFactory implements ManagedConnectionFactory, FirebirdConnectionProperties, - Serializable { - - private static final long serialVersionUID = 7500832904323015501L; +public class FBManagedConnectionFactory implements FirebirdConnectionProperties, Serializable { + + // This class uses a serialization proxy, see class at end of file /** - * The 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. *

* It is also used to return a canonical instance to {@link org.firebirdsql.jdbc.FBDriver}. *

@@ -85,40 +72,47 @@ public class FBManagedConnectionFactory implements ManagedConnectionFactory, Fir private static final Map> mcfInstances = new ConcurrentHashMap<>(); private static final ReferenceQueue mcfReferenceQueue = new ReferenceQueue<>(); + /** + * Used to ensure that instances with a different connection manager type are different in the {@code mcfInstances} + * map. + */ + private static final String DEFAULT_CONNECTION_MANAGER_TYPE = "CONNECTION_MANAGER_TYPE"; - private ConnectionManager defaultCm; + private XcaConnectionManager defaultCm; private int hashCode; private GDSType gdsType; // Maps supplied XID to internal transaction handle. - private transient final Map xidMap = new ConcurrentHashMap<>(); + private final Map xidMap = new ConcurrentHashMap<>(); - private transient final Object startLock = new Object(); - private transient boolean started = false; + private final Object startLock = new Object(); + private boolean started = false; - private FBConnectionProperties connectionProperties; + private final FBConnectionProperties connectionProperties; /** * Create a new pure-Java FBManagedConnectionFactory. */ public FBManagedConnectionFactory() { - this(GDSFactory.getDefaultGDSType(), new FBConnectionProperties()); + this(GDSFactory.getDefaultGDSType(), null); } /** * Create a new FBManagedConnectionFactory based around the given GDSType. - * + * * @param gdsType - * The GDS implementation to use + * The GDS implementation to use */ public FBManagedConnectionFactory(GDSType gdsType) { - this(gdsType, new FBConnectionProperties()); + this(gdsType, null); } - + public FBManagedConnectionFactory(GDSType gdsType, FBConnectionProperties connectionProperties) { - this.defaultCm = new FBStandAloneConnectionManager(); - this.connectionProperties = connectionProperties; + this.connectionProperties = connectionProperties != null + ? (FBConnectionProperties) connectionProperties.clone() + : new FBConnectionProperties(); setType(gdsType.toString()); + setDefaultConnectionManager(new FBStandAloneConnectionManager()); } public FbDatabaseFactory getDatabaseFactory() { @@ -127,83 +121,18 @@ public FbDatabaseFactory getDatabaseFactory() { /** * Get the GDS implementation type around which this factory is based. - * + * * @return The GDS implementation type */ public GDSType getGDSType() { - if (gdsType != null) + if (gdsType != null) { return gdsType; - + } + gdsType = GDSType.getType(getType()); - return gdsType; } - /** - * @deprecated use {@link #getBlobBufferSize()} - */ - @Deprecated - public int getBlobBufferLength() { - return getBlobBufferSize(); - } - - /** - * @deprecated use {@link #setBlobBufferSize(int)} - */ - @Deprecated - public void setBlobBufferLength(int value) { - setBlobBufferSize(value); - } - - /** - * @deprecated use {@link #getDefaultTransactionIsolation()} - */ - @Deprecated - public Integer getTransactionIsolation() { - return getDefaultTransactionIsolation(); - } - - /** - * @deprecated use {@link #setDefaultTransactionIsolation(int)} - */ - @Deprecated - public void setTransactionIsolation(Integer value) { - if (value != null) - setDefaultTransactionIsolation(value); - } - - /** - * @deprecated use {@link #getDefaultIsolation()} - */ - @Deprecated - public String getTransactionIsolationName() { - return getDefaultIsolation(); - } - - /** - * @deprecated use {@link #setDefaultIsolation(String)} - */ - @Deprecated - public void setTransactionIsolationName(String name) { - setDefaultIsolation(name); - } - - /** - * @deprecated use {@link #getCharSet()} instead. - */ - @Deprecated - public String getLocalEncoding() { - return getCharSet(); - } - - /** - * @deprecated use {@link #setCharSet(String)} instead. - */ - @Deprecated - public void setLocalEncoding(String localEncoding) { - setCharSet(localEncoding); - } - public int getBlobBufferSize() { return connectionProperties.getBlobBufferSize(); } @@ -309,7 +238,7 @@ public void setEncoding(String encoding) { } public void setNonStandardProperty(String key, String value) { - connectionProperties.setNonStandardProperty(key, value); + connectionProperties.setNonStandardProperty(key, value); } public void setNonStandardProperty(String propertyMapping) { @@ -321,11 +250,11 @@ public void setPassword(String password) { } public void setRoleName(String roleName) { - connectionProperties.setRoleName(roleName); + connectionProperties.setRoleName(roleName); } public void setSocketBufferSize(int socketBufferSize) { - connectionProperties.setSocketBufferSize(socketBufferSize); + connectionProperties.setSocketBufferSize(socketBufferSize); } public void setSqlDialect(String sqlDialect) { @@ -333,31 +262,31 @@ public void setSqlDialect(String sqlDialect) { } public void setTimestampUsesLocalTimezone(boolean timestampUsesLocalTimezone) { - connectionProperties.setTimestampUsesLocalTimezone(timestampUsesLocalTimezone); + connectionProperties.setTimestampUsesLocalTimezone(timestampUsesLocalTimezone); } public void setTpbMapping(String tpbMapping) { - connectionProperties.setTpbMapping(tpbMapping); + connectionProperties.setTpbMapping(tpbMapping); } public void setTransactionParameters(int isolation, TransactionParameterBuffer tpb) { - connectionProperties.setTransactionParameters(isolation, tpb); + connectionProperties.setTransactionParameters(isolation, tpb); } public void setType(String type) { - if (gdsType != null) - throw new java.lang.IllegalStateException( - "Cannot change GDS type at runtime."); - + if (gdsType != null) { + throw new IllegalStateException("Cannot change GDS type at runtime."); + } + connectionProperties.setType(type); } public void setUserName(String userName) { - connectionProperties.setUserName(userName); + connectionProperties.setUserName(userName); } public void setUseStreamBlobs(boolean useStreamBlobs) { - connectionProperties.setUseStreamBlobs(useStreamBlobs); + connectionProperties.setUseStreamBlobs(useStreamBlobs); } public boolean isDefaultResultSetHoldable() { @@ -368,10 +297,12 @@ public void setDefaultResultSetHoldable(boolean isHoldable) { connectionProperties.setDefaultResultSetHoldable(isHoldable); } - public void setDefaultConnectionManager(ConnectionManager defaultCm) { + public void setDefaultConnectionManager(XcaConnectionManager defaultCm) { + // Ensures that instances with different connection managers do not resolve to the same connection manager + connectionProperties.setNonStandardProperty(DEFAULT_CONNECTION_MANAGER_TYPE, defaultCm.getClass().getName()); this.defaultCm = defaultCm; } - + public int getSoTimeout() { return connectionProperties.getSoTimeout(); } @@ -379,11 +310,11 @@ public int getSoTimeout() { public void setSoTimeout(int soTimeout) { connectionProperties.setSoTimeout(soTimeout); } - + public int getConnectTimeout() { return connectionProperties.getConnectTimeout(); } - + public void setConnectTimeout(int connectTimeout) { connectionProperties.setConnectTimeout(connectTimeout); } @@ -478,21 +409,26 @@ public void setWireCompression(boolean wireCompression) { connectionProperties.setWireCompression(wireCompression); } + @Override public int hashCode() { - if (hashCode != 0) + if (hashCode != 0) { return hashCode; + } int result = 17; result = 37 * result + connectionProperties.hashCode(); - if (result == 0) + if (result == 0) { result = 17; - - if (gdsType != null) + } + + if (gdsType != null) { hashCode = result; - + } + return result; } + @Override public boolean equals(Object other) { if (other == this) return true; @@ -502,193 +438,91 @@ public boolean equals(Object other) { return this.connectionProperties.equals(that.connectionProperties); } - - public FBConnectionRequestInfo getDefaultConnectionRequestInfo() throws ResourceException { - try { - return new FBConnectionRequestInfo(getDatabaseParameterBuffer().deepCopy()); - } catch(SQLException ex) { - throw new FBResourceException(ex); - } + + public FBConnectionRequestInfo getDefaultConnectionRequestInfo() throws SQLException { + return new FBConnectionRequestInfo(getDatabaseParameterBuffer().deepCopy()); } - - public FBTpb getDefaultTpb() throws ResourceException { - int defaultTransactionIsolation = - connectionProperties.getMapper().getDefaultTransactionIsolation(); + public FBTpb getDefaultTpb() throws SQLException { + int defaultTransactionIsolation = + connectionProperties.getMapper().getDefaultTransactionIsolation(); + return getTpb(defaultTransactionIsolation); } - public FBTpb getTpb(int defaultTransactionIsolation) throws FBResourceException { + public FBTpb getTpb(int defaultTransactionIsolation) throws SQLException { return new FBTpb(connectionProperties.getMapper().getMapping( defaultTransactionIsolation)); } /** - * The 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. - *

- * This method returns a 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. *

- * The 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. + *

+ * + * @param connectionRequestInfo + * Additional resource adapter specific connection request information, can be {@code null} for default + * @return Managed connection instance + * @throws SQLException + * generic exception + * @see #createManagedConnection() */ - public void setLogWriter(PrintWriter out) throws ResourceException { - // ignore - we are using alternative logging + public FBManagedConnection createManagedConnection(FBConnectionRequestInfo connectionRequestInfo) + throws SQLException { + start(); + return new FBManagedConnection(connectionRequestInfo, this); } - /** - * Get 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. - * - * @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. - *

- * Example: - *

- * try {
- *     // execute some code here
- *     ...
- * } catch(GDSException gdsex) {
- *     throw new FBResourceException(gdsex);
- * }
- * 
- *

- * - * @author Roman Rokytskyy - */ -@SuppressWarnings("deprecation") -public class FBResourceException extends ResourceException { - - /* Some versions of JCA will call initCause from setLinkedException. A subsequent call to initCause will then - * throw an IllegalStateException. - * We check this behavior once, so we don't need to check if cause is null for each exception. - */ - private static final boolean PERFORM_EXPLICIT_INIT_CAUSE; - static { - ResourceException resourceException = new ResourceException(); - resourceException.setLinkedException(new RuntimeException()); - PERFORM_EXPLICIT_INIT_CAUSE = resourceException.getCause() == null; - } - - /** - * Create a new instance of {@code FBResourceException} with a given string message and generic error code. - * - * @param reason - * The string message for the exception - */ - public FBResourceException(String reason) { - super(reason, SQLStateConstants.SQL_STATE_GENERAL_ERROR); - } - - /** - * Create a new instance of {@code FBResourceException} with a message and specific error code. - * - * @param reason - * The string message for the exception - * @param errorCode - * The error code for the cause of the exception - */ - public FBResourceException(String reason, String errorCode) { - super(reason, errorCode); - } - - /** - * Create a new instance of {@code FBResourceException} with a generic error code that is linked to another - * (sub) exception. - * - * @param reason - * The string message for the exception - * @param original - * The original exception to which this instance is to - * be linked to - */ - public FBResourceException(String reason, Exception original) { - super(reason, SQLStateConstants.SQL_STATE_GENERAL_ERROR); - // Preserve setLinkedException for backwards compatibility - setLinkedException(original); - if (PERFORM_EXPLICIT_INIT_CAUSE) { - initCause(original); - } - if (original instanceof SQLException) { - SQLException origSql = (SQLException) original; - if (origSql.getSQLState() != null) { - setErrorCode(origSql.getSQLState()); - } - } - } - - /** - * Create a new instance of {@code FBResourceException} with a generic error code that is linked to another - * (sub) exception. - * - * @param original - * The original exception to which this instance is - * to be linked to - */ - public FBResourceException(Exception original) { - this(original.getMessage(), original); - } - - /** - * Get message of this exception. - * - * @return combined message of this exception and original exception. - */ - public String getMessage() { - String message = super.getMessage(); - String causeMessage = null; - - if (getCause() != null) { - causeMessage = getCause().getMessage(); - } else if (getLinkedException() != null) { - causeMessage = getLinkedException().getMessage(); - } - - if (causeMessage == null) { - return message; - } - - if (message == null) { - return causeMessage; - } - - return message + "\nReason: " + causeMessage; - } - - - /** - * Print the stack trace of this exception to {@code STDERR} - */ - public void printStackTrace() { - printStackTrace(System.err); - } - - /** - * Print the stack trace of this exception to a given {@code PrintStream} - * - * @param s - * The {@code PrintStream} to which to write the stack trace - */ - public void printStackTrace(PrintStream s) { - super.printStackTrace(s); - if (getLinkedException() != null) { - s.print("at "); - getLinkedException().printStackTrace(s); - } - } - - /** - * Print the stack trace of this exception to a given {@code PrintWriter} - * - * @param s - * The {@code PrintWriter} to which to write the stack trace - */ - public void printStackTrace(PrintWriter s) { - super.printStackTrace(s); - if (getLinkedException() != null) { - s.print("at "); - getLinkedException().printStackTrace(s); - } - } -} \ No newline at end of file diff --git a/src/main/org/firebirdsql/jaybird/xca/FBResourceTransactionException.java b/src/main/org/firebirdsql/jaybird/xca/FBResourceTransactionException.java deleted file mode 100644 index a862ab45f9..0000000000 --- a/src/main/org/firebirdsql/jaybird/xca/FBResourceTransactionException.java +++ /dev/null @@ -1,77 +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; - -/** - * Exception represents transaction error in resource. - * - * @author Roman Rokytskyy - */ -public class FBResourceTransactionException extends FBResourceException { - - /** - * Create a new instance of {@code FBResourceTransactionException} with a given message and generic error code - * - * @param reason - * The string message for this exception - */ - public FBResourceTransactionException(String reason) { - super(reason); - } - - /** - * Create a new instance of {@code FBResourceTransactionException} with a given message and error code. - * - * @param reason - * The string message for this exception - * @param errorCode - * The error code for this exception - */ - public FBResourceTransactionException(String reason, String errorCode) { - super(reason, errorCode); - } - - /** - * Create a new instance of {@code FBResourceTransactionException} with a given message and sub-exception. - * - * @param reason - * The string message for this exception - * @param cause - * The underlying exception - */ - public FBResourceTransactionException(String reason, Exception cause) { - super(reason, cause); - } - - /** - * Create a new instance of {@code FBResourceException} with a given message, error code and underlying exception. - * - * @param reason - * The string message for this exception - * @param errorCode - * The error code for this exception - * @param cause - * The underlying exception - */ - public FBResourceTransactionException(String reason, String errorCode, Exception cause) { - super(reason, cause); - setErrorCode(errorCode); - } - -} diff --git a/src/main/org/firebirdsql/jaybird/xca/FBStandAloneConnectionManager.java b/src/main/org/firebirdsql/jaybird/xca/FBStandAloneConnectionManager.java index 3640e757a1..d65e9afb24 100644 --- a/src/main/org/firebirdsql/jaybird/xca/FBStandAloneConnectionManager.java +++ b/src/main/org/firebirdsql/jaybird/xca/FBStandAloneConnectionManager.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,106 +18,55 @@ */ package org.firebirdsql.jaybird.xca; -import java.io.Serializable; - -import javax.resource.ResourceException; -import javax.resource.spi.*; - +import org.firebirdsql.jdbc.FirebirdConnection; import org.firebirdsql.logging.Logger; import org.firebirdsql.logging.LoggerFactory; +import java.io.Serializable; +import java.sql.SQLException; + /** - * The class 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. *

* 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). - * + *

+ * * @author Roman Rokytskyy */ class FatalGDSErrorHelper { /** - * Check whether the specified exception is fatal from the JCA point of - * view. - * - * @param ex exception to check. - * - * @return 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. + *

* This list has been kindly reviewed by Ann Harrison, 12/13/2002 + *

*/ static final int[] FATAL_ERRORS = new int[] { - ISCConstants.isc_network_error, - ISCConstants.isc_net_read_err, - ISCConstants.isc_net_write_err, + ISCConstants.isc_network_error, + ISCConstants.isc_net_read_err, + ISCConstants.isc_net_write_err, // ISCConstants.isc_bad_db_format, //probably not a firebird db // ISCConstants.isc_bad_db_handle, //couldn't get a connection // ISCConstants.isc_bad_dpb_content, //couldn't get a connection // ISCConstants.isc_bad_dpb_form, //couldn't get a connection // ISCConstants.isc_bug_check, // ISCConstants.isc_db_corrupt, - ISCConstants.isc_io_error, + ISCConstants.isc_io_error, // ISCConstants.isc_metadata_corrupt, // // ISCConstants.isc_open_trans, //could not forcibly close tx on server shutdown. @@ -95,8 +78,8 @@ static boolean isFatal(SQLException ex) { // ISCConstants.isc_port_len, //user sent buffer too short or long for data // //expected. Should never occur // - ISCConstants.isc_req_sync, //client asked for data when server expected - //data or vice versa. Should never happen + ISCConstants.isc_req_sync, //client asked for data when server expected + //data or vice versa. Should never happen // // ISCConstants.isc_req_wrong_db,//In a multi-database application, a prepared // //request has been opened against the wrong @@ -150,8 +133,8 @@ static boolean isFatal(SQLException ex) { // ISCConstants.isc_bad_protocol, // ISCConstants.isc_file_in_use }; - + static { - Arrays.sort(FATAL_ERRORS); + Arrays.sort(FATAL_ERRORS); } } diff --git a/src/main/org/firebirdsql/jaybird/xca/FirebirdLocalTransaction.java b/src/main/org/firebirdsql/jaybird/xca/FirebirdLocalTransaction.java deleted file mode 100644 index 15be241fc2..0000000000 --- a/src/main/org/firebirdsql/jaybird/xca/FirebirdLocalTransaction.java +++ /dev/null @@ -1,50 +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.ResourceException; -import javax.resource.spi.LocalTransaction; -import javax.transaction.xa.Xid; - - -/** - * Extention of the {@link javax.resource.spi.LocalTransaction} interface - * to tell whether the underlying managed connection is currently participating - * in some transaction or not and to obtain the associated Xid. - */ -public interface FirebirdLocalTransaction extends LocalTransaction { - - /** - * Check if managed connection is currently participating in transaction. - * - * @return 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: + *
    + *
  • Type of the connection event
  • + *
  • {@link FBManagedConnection} instance that generated the connection event. A {@code FBManagedConnection} + * instance is returned from the method {@link #getSource()}
  • + *
  • Connection handle associated with the {@code FBManagedConnection} instance; required for the + * {@code CONNECTION_CLOSED} event and optional for the other event types
  • + *
  • Optionally, an exception indicating the connection related error. Note that exception is used for + * {@code CONNECTION_ERROR_OCCURRED}
  • + *
+ */ +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. + *

+ * 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. + *

+ * + * @param connectionEvent + * Connection event + */ + void connectionClosed(XcaConnectionEvent connectionEvent); + + /** + * Notifies a connection related error. + *

+ * 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. + *

+ * + * @param connectionEvent + * Connection event + */ + void connectionErrorOccurred(XcaConnectionEvent connectionEvent); + +} diff --git a/src/main/org/firebirdsql/jaybird/xca/XcaConnectionManager.java b/src/main/org/firebirdsql/jaybird/xca/XcaConnectionManager.java new file mode 100644 index 0000000000..750719cd07 --- /dev/null +++ b/src/main/org/firebirdsql/jaybird/xca/XcaConnectionManager.java @@ -0,0 +1,49 @@ +/* + * 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; + +import java.sql.SQLException; + +/** + * The {@code XcaConnectionManager} can be used to modify the configuration of a connection, provide pooling or other + * behaviour when allocating a new connection. + */ +public interface XcaConnectionManager extends java.io.Serializable { + + /** + * Allocates a new {@link FirebirdConnection} backed by a {@link FBManagedConnection} from the specified factory. + *

+ * The returned connection should behave as a new connection, but may be backed by an already established (eg + * pooled) managed connection. + *

+ * + * @param managedConnectionFactory + * Managed connection factory + * @param connectionRequestInfo + * Specific connection request info + * @return A new {@code FirebirdConnection} instance + * @throws SQLException + * for generic exceptions + */ + FirebirdConnection allocateConnection(FBManagedConnectionFactory managedConnectionFactory, + FBConnectionRequestInfo connectionRequestInfo) throws SQLException; + +} diff --git a/src/main/org/firebirdsql/jaybird/xca/package-info.java b/src/main/org/firebirdsql/jaybird/xca/package-info.java index fad3ab64ca..aea329c75c 100644 --- a/src/main/org/firebirdsql/jaybird/xca/package-info.java +++ b/src/main/org/firebirdsql/jaybird/xca/package-info.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 @@ -23,6 +23,10 @@ * Historically it was derived from the JavaEE Connector Architecture specification, but that tie has been cut * since Jaybird 5. *

+ *

+ * 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. + *

*/ @InternalApi package org.firebirdsql.jaybird.xca; diff --git a/src/main/org/firebirdsql/jdbc/FBConnection.java b/src/main/org/firebirdsql/jdbc/FBConnection.java index 2d15c20c74..e5af475b2b 100644 --- a/src/main/org/firebirdsql/jdbc/FBConnection.java +++ b/src/main/org/firebirdsql/jdbc/FBConnection.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 @@ -26,25 +26,19 @@ import org.firebirdsql.gds.impl.GDSHelper; import org.firebirdsql.gds.ng.FbDatabase; import org.firebirdsql.gds.ng.FbExceptionBuilder; -import org.firebirdsql.jaybird.xca.FBConnectionRequestInfo; -import org.firebirdsql.jaybird.xca.FBLocalTransaction; -import org.firebirdsql.jaybird.xca.FBManagedConnection; -import org.firebirdsql.jaybird.xca.FirebirdLocalTransaction; +import org.firebirdsql.jaybird.xca.*; import org.firebirdsql.jdbc.escape.FBEscapedParser; import org.firebirdsql.logging.Logger; import org.firebirdsql.logging.LoggerFactory; import org.firebirdsql.util.SQLExceptionChainBuilder; -import javax.resource.ResourceException; import java.lang.ref.WeakReference; import java.sql.*; import java.util.*; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import static org.firebirdsql.gds.impl.DatabaseParameterBufferExtension.GENERATED_KEYS_ENABLED; -import static org.firebirdsql.gds.impl.DatabaseParameterBufferExtension.IGNORE_PROCEDURE_TYPE; -import static org.firebirdsql.gds.impl.DatabaseParameterBufferExtension.USE_FIREBIRD_AUTOCOMMIT; +import static org.firebirdsql.gds.impl.DatabaseParameterBufferExtension.*; /** * The class 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 Set activeStatements = Collections.synchronizedSet(new HashSet()); + protected final Set activeStatements = Collections.synchronizedSet(new HashSet<>()); - private int resultSetHoldability = ResultSet.CLOSE_CURSORS_AT_COMMIT; + private int resultSetHoldability; private StoredProcedureMetaData storedProcedureMetaData; private GeneratedKeysSupport generatedKeysSupport; @@ -268,15 +262,12 @@ public void setTransactionParameters(int isolationLevel, TransactionParameterBuf public void setTransactionParameters(TransactionParameterBuffer tpb) throws SQLException { synchronized (getSynchronizationObject()) { checkValidity(); - try { - if (getLocalTransaction().inTransaction()) { - throw new FBSQLException("Cannot set transaction parameters when transaction is already started."); - } - - mc.setTransactionParameters(tpb); - } catch (ResourceException ex) { - throw new FBSQLException(ex); + if (getLocalTransaction().inTransaction()) { + // TODO More specific exception, jaybird error code + throw new FBSQLException("Cannot set transaction parameters when transaction is already started."); } + + mc.setTransactionParameters(tpb); } } @@ -502,15 +493,12 @@ public DatabaseMetaData getMetaData() throws SQLException { public void setReadOnly(boolean readOnly) throws SQLException { synchronized (getSynchronizationObject()) { checkValidity(); - try { - if (getLocalTransaction().inTransaction() && !mc.isManagedEnvironment()) - throw new FBSQLException("Calling setReadOnly(boolean) method " + - "is not allowed when transaction is already started."); - - mc.setReadOnly(readOnly); - } catch (ResourceException ex) { - throw new FBSQLException(ex); + if (getLocalTransaction().inTransaction() && !mc.isManagedEnvironment()) { + // TODO More specific exception, jaybird error code + throw new FBSQLException("Calling setReadOnly(boolean) method " + + "is not allowed when transaction is already started."); } + mc.setReadOnly(readOnly); } } @@ -548,16 +536,10 @@ public String getCatalog() throws SQLException { public void setTransactionIsolation(int level) throws SQLException { synchronized (getSynchronizationObject()) { checkValidity(); - - try { - if (!getAutoCommit() && !mc.isManagedEnvironment()) - txCoordinator.commit(); - - mc.setTransactionIsolation(level); - - } catch (ResourceException re) { - throw new FBSQLException(re); + if (!getAutoCommit() && !mc.isManagedEnvironment()) { + txCoordinator.commit(); } + mc.setTransactionIsolation(level); } } @@ -565,11 +547,7 @@ public void setTransactionIsolation(int level) throws SQLException { public int getTransactionIsolation() throws SQLException { synchronized (getSynchronizationObject()) { checkValidity(); - try { - return mc.getTransactionIsolation(); - } catch (ResourceException e) { - throw new FBSQLException(e); - } + return mc.getTransactionIsolation(); } } @@ -641,10 +619,12 @@ private void checkHoldability(int resultSetType, int resultSetHoldability) throw boolean notScrollable = resultSetType != ResultSet.TYPE_SCROLL_INSENSITIVE; - if (holdable && notScrollable) + if (holdable && notScrollable) { + // TODO jaybird error code throw new FBDriverNotCapableException( "Holdable cursors are supported only " + "for scrollable insensitive result sets."); + } } @Override @@ -966,18 +946,15 @@ protected void invalidateSavepoints() { } } - //------------------------------------------- - //Borrowed from javax.resource.cci.Connection - /** * Returns a FBLocalTransaction instance that enables a component to * demarcate resource manager local transactions on this connection. */ - public FirebirdLocalTransaction getLocalTransaction() { + public FBLocalTransaction getLocalTransaction() { synchronized (getSynchronizationObject()) { - if (localTransaction == null) - localTransaction = new FBLocalTransaction(mc, this); - + if (localTransaction == null) { + localTransaction = mc.getLocalTransaction(); + } return localTransaction; } } @@ -1140,7 +1117,7 @@ else if (sessionContext != null) return null; } - + } @Override diff --git a/src/main/org/firebirdsql/jdbc/FBConnectionProperties.java b/src/main/org/firebirdsql/jdbc/FBConnectionProperties.java index d66fdb6529..c8071cd6b4 100644 --- a/src/main/org/firebirdsql/jdbc/FBConnectionProperties.java +++ b/src/main/org/firebirdsql/jdbc/FBConnectionProperties.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,7 +24,6 @@ import org.firebirdsql.gds.ParameterBufferHelper; import org.firebirdsql.gds.TransactionParameterBuffer; import org.firebirdsql.gds.impl.DatabaseParameterBufferImp; -import org.firebirdsql.jaybird.xca.FBResourceException; import java.io.Serializable; import java.sql.Connection; @@ -535,7 +534,7 @@ public void setTransactionParameters(int isolation, TransactionParameterBuffer t } } - public FBTpbMapper getMapper() throws FBResourceException { + public FBTpbMapper getMapper() throws SQLException { if (mapper != null) { return mapper; } diff --git a/src/main/org/firebirdsql/jdbc/FBDataSource.java b/src/main/org/firebirdsql/jdbc/FBDataSource.java index 5368040360..80308eaadd 100644 --- a/src/main/org/firebirdsql/jdbc/FBDataSource.java +++ b/src/main/org/firebirdsql/jdbc/FBDataSource.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,101 +21,55 @@ import org.firebirdsql.ds.RootCommonDataSource; import org.firebirdsql.jaybird.xca.FBConnectionRequestInfo; import org.firebirdsql.jaybird.xca.FBManagedConnectionFactory; +import org.firebirdsql.jaybird.xca.XcaConnectionManager; +import org.firebirdsql.util.InternalApi; -import javax.naming.Reference; -import javax.resource.Referenceable; -import javax.resource.ResourceException; -import javax.resource.spi.ConnectionManager; -import javax.resource.spi.ManagedConnectionFactory; import javax.sql.DataSource; import java.io.Serializable; import java.sql.Connection; import java.sql.SQLException; /** - * The class FBDataSource 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. + *

+ * 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. + *

* * @author David Jencks - * @version 1.0 */ -public class FBDataSource extends RootCommonDataSource implements DataSource, Serializable, Referenceable { +@InternalApi +public class FBDataSource extends RootCommonDataSource implements DataSource, Serializable { private static final long serialVersionUID = 1178461472062969634L; - private final ConnectionManager cm; - + private final XcaConnectionManager cm; private final FBManagedConnectionFactory mcf; - private Reference jndiReference; - private int loginTimeout = 0; - // this constructor is needed to make BES happy. - public FBDataSource(ManagedConnectionFactory mcf, ConnectionManager cm) { - this((FBManagedConnectionFactory) mcf, cm); - } - - public FBDataSource(FBManagedConnectionFactory mcf, ConnectionManager cm) { + public FBDataSource(FBManagedConnectionFactory mcf, XcaConnectionManager cm) { this.mcf = mcf; this.cm = cm; } - /** - * Set the JNDI 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 T unwrap(Class iface) throws SQLException { - if (!isWrapperFor(iface)) + if (!isWrapperFor(iface)) { throw new SQLException("Unable to unwrap to class " + iface.getName()); + } return iface.cast(this); } diff --git a/src/main/org/firebirdsql/jdbc/FBDatabaseMetaData.java b/src/main/org/firebirdsql/jdbc/FBDatabaseMetaData.java index d073be43fa..51327f0ccc 100644 --- a/src/main/org/firebirdsql/jdbc/FBDatabaseMetaData.java +++ b/src/main/org/firebirdsql/jdbc/FBDatabaseMetaData.java @@ -223,6 +223,7 @@ public String getDatabaseProductVersion() throws SQLException { @Override public String getDriverName() throws SQLException { + // Retain JCA in name for compatibility with tools that consult metadata and use this string return "Jaybird JCA/JDBC driver"; } diff --git a/src/main/org/firebirdsql/jdbc/FBDriver.java b/src/main/org/firebirdsql/jdbc/FBDriver.java index 7cd1835b1b..edaac6dba1 100644 --- a/src/main/org/firebirdsql/jdbc/FBDriver.java +++ b/src/main/org/firebirdsql/jdbc/FBDriver.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 @@ -28,7 +28,6 @@ import org.firebirdsql.logging.Logger; import org.firebirdsql.logging.LoggerFactory; -import javax.resource.ResourceException; import java.io.UnsupportedEncodingException; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; @@ -114,12 +113,12 @@ public Connection connect(String url, final Properties info) throws SQLException return dataSource.getConnection(mcf.getUserName(), mcf.getPassword()); - } catch (ResourceException | GDSException resex) { - throw new FBSQLException(resex); + } catch (GDSException e) { + throw new FBSQLException(e); } } - private FBDataSource createDataSource(final FBManagedConnectionFactory mcf) throws ResourceException { + private FBDataSource createDataSource(final FBManagedConnectionFactory mcf) { final FBConnectionProperties cacheKey = mcf.getCacheKey(); FBDataSource dataSource = dataSourceFromCache(cacheKey); if (dataSource != null) return dataSource; @@ -153,20 +152,14 @@ private FBDataSource dataSourceFromCache(final FBConnectionProperties cacheKey) @Override public FirebirdConnection connect(FirebirdConnectionProperties properties) throws SQLException { GDSType type = GDSType.getType(properties.getType()); - - if (type == null) + if (type == null) { type = GDSFactory.getDefaultGDSType(); - try { - FBManagedConnectionFactory mcf = new FBManagedConnectionFactory(type); - - mcf = mcf.canonicalize(); - - FBDataSource dataSource = createDataSource(mcf); - - return (FirebirdConnection) dataSource.getConnection(mcf.getUserName(), mcf.getPassword()); - } catch (ResourceException ex) { - throw new FBSQLException(ex); } + + FBManagedConnectionFactory mcf = new FBManagedConnectionFactory(type, (FBConnectionProperties) properties) + .canonicalize(); + FBDataSource dataSource = createDataSource(mcf); + return (FirebirdConnection) dataSource.getConnection(mcf.getUserName(), mcf.getPassword()); } @Override diff --git a/src/main/org/firebirdsql/jdbc/FBSQLException.java b/src/main/org/firebirdsql/jdbc/FBSQLException.java index 461474c3e9..bd6fbf53eb 100644 --- a/src/main/org/firebirdsql/jdbc/FBSQLException.java +++ b/src/main/org/firebirdsql/jdbc/FBSQLException.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,6 @@ import java.sql.SQLException; -import javax.resource.ResourceException; - -import org.firebirdsql.gds.GDSException; -import org.firebirdsql.jaybird.xca.FBResourceException; -import org.firebirdsql.jaybird.xca.FBXAException; - public class FBSQLException extends SQLException { private static final long serialVersionUID = 8157410954186424083L; @@ -35,15 +29,6 @@ public FBSQLException(Exception ex) { initCause(ex); } - public FBSQLException(GDSException ex) { - super(createGDSExceptionMessage(ex), defaultSQLStateIfNull(ex.getSQLState()), ex.getIntParam(), ex); - } - - public FBSQLException(ResourceException ex) { - super(createResourceMessage(ex), defaultSQLStateIfNull(ex.getErrorCode()), getSqlErrorCode(ex), resolveCause(ex)); - // try to unwrap wrapped GDS exception, in this case FBResourceException will never appear on the stack - } - public FBSQLException(String message) { super(message, SQLStateConstants.SQL_STATE_GENERAL_ERROR); } @@ -84,74 +69,6 @@ public Exception getInternalException() { return (Exception) getCause(); } - /** - * Helper method to create message text for constructor accepting - * ResourceException ({@link #FBSQLException(ResourceException)}) - * - * @param ex - * ResourceException - * @return Exception message - */ - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - private static String createResourceMessage(ResourceException ex) { - Throwable cause = resolveCause(ex); - if (cause instanceof GDSException) { - return createGDSExceptionMessage((GDSException) cause); - } - return "Resource Exception. " + ex.getMessage(); - } - - /** - * Helper method to create message text for GDSException. - * - * @param ex - * The GDSException - * @return Message text - */ - private static String createGDSExceptionMessage(GDSException ex) { - return "GDS Exception. " + ex.getIntParam() + ". " + ex.getMessage(); - } - - /** - * Helper method to get the SQL vendor code (or in the case of Firebird: the - * isc errorcode). - * - * @param ex - * ResourceException - * @return isc errorcode, or 0 - */ - private static int getSqlErrorCode(ResourceException ex) { - Throwable cause = resolveCause(ex); - if (cause instanceof GDSException) { - return ((GDSException) cause).getIntParam(); - } - if (cause instanceof SQLException) { - return ((SQLException) cause).getErrorCode(); - } - if (cause instanceof FBXAException) { - FBXAException fbXaException = (FBXAException) cause; - Throwable cause2 = fbXaException.getCause(); - if (cause2 instanceof SQLException) { - return ((SQLException) cause2).getErrorCode(); - } - } - return 0; - } - - /** - * @param ex - * ResourceException - * @return Non-null exception linked to FBResourceException, or the original - * (FB)ResourceException. - */ - @SuppressWarnings("deprecation") - private static Throwable resolveCause(ResourceException ex) { - if (ex instanceof FBResourceException && ex.getLinkedException() != null) { - return ex.getLinkedException(); - } - return ex; - } - /** * @param sqlState * SQL State value (or null) diff --git a/src/main/org/firebirdsql/jdbc/FBTpbMapper.java b/src/main/org/firebirdsql/jdbc/FBTpbMapper.java index ea98a65b89..5725ed240c 100644 --- a/src/main/org/firebirdsql/jdbc/FBTpbMapper.java +++ b/src/main/org/firebirdsql/jdbc/FBTpbMapper.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 @@ -22,10 +22,10 @@ import org.firebirdsql.gds.ParameterBufferHelper; import org.firebirdsql.gds.TransactionParameterBuffer; import org.firebirdsql.gds.impl.TransactionParameterBufferImpl; -import org.firebirdsql.jaybird.xca.FBResourceException; import java.io.Serializable; import java.sql.Connection; +import java.sql.SQLException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -208,9 +208,9 @@ public FBTpbMapper() { * Meaning of these constants and possible combinations you can find in a * documentation. * - * @throws FBResourceException if mapping contains incorrect values. + * @throws SQLException if mapping contains incorrect values. */ - public FBTpbMapper(Map stringMapping) throws FBResourceException { + public FBTpbMapper(Map stringMapping) throws SQLException { this(); processMapping(stringMapping); } @@ -221,17 +221,18 @@ public FBTpbMapper(Map stringMapping) throws FBResourceException * * @param stringMapping * mapping to process. - * @throws FBResourceException + * @throws SQLException * if mapping contains incorrect values. */ - private void processMapping(Map stringMapping) throws FBResourceException { + private void processMapping(Map stringMapping) throws SQLException { for (Map.Entry entry : stringMapping.entrySet()) { String jdbcTxIsolation = entry.getKey(); - Integer isolationLevel; + int isolationLevel; try { isolationLevel = getTransactionIsolationLevel(jdbcTxIsolation); } catch (IllegalArgumentException ex) { - throw new FBResourceException("Transaction isolation " + jdbcTxIsolation + " is not supported."); + // TODO More specific exception, Jaybird error code + throw new SQLException("Transaction isolation " + jdbcTxIsolation + " is not supported."); } TransactionParameterBuffer tpb = processMapping(entry.getValue()); mapping.put(isolationLevel, tpb); @@ -239,18 +240,16 @@ private void processMapping(Map stringMapping) throws FBResource } /** - * Create instance of this class and load mapping from the specified - * resource. + * Create instance of this class and load mapping from the specified resource. * * @param mappingResource * name of the resource to load. * @param cl * class loader that should be used to load specified resource. - * @throws FBResourceException - * if resource cannot be loaded or contains - * incorrect values. + * @throws SQLException + * if resource cannot be loaded or contains incorrect values. */ - public FBTpbMapper(String mappingResource, ClassLoader cl) throws FBResourceException { + public FBTpbMapper(String mappingResource, ClassLoader cl) throws SQLException { try { ResourceBundle res = ResourceBundle.getBundle(mappingResource, Locale.getDefault(), cl); @@ -266,7 +265,8 @@ public FBTpbMapper(String mappingResource, ClassLoader cl) throws FBResourceExce processMapping(mapping); } catch (MissingResourceException mrex) { - throw new FBResourceException("Cannot load TPB mapping." + mrex.getMessage()); + // TODO More specific exception, Jaybird error code + throw new SQLException("Cannot load TPB mapping. " + mrex.getMessage(), mrex); } } @@ -286,11 +286,11 @@ public FBTpbMapper(String mappingResource, ClassLoader cl) throws FBResourceExce * FirebirdConnectionProperties to set transaction state * @param info * connection parameters passed into a driver. - * @throws FBResourceException + * @throws SQLException * if specified mapping is incorrect. */ public static void processMapping(FirebirdConnectionProperties connectionProperties, Properties info) - throws FBResourceException { + throws SQLException { for (String isolationName : ISOLATION_LEVEL_NAMES) { String property = info.getProperty(isolationName); if (property == null) continue; @@ -307,11 +307,10 @@ public static void processMapping(FirebirdConnectionProperties connectionPropert * @param mapping * comma-separated list of keywords. * @return set containing values corresponding to the specified keywords. - * @throws FBResourceException - * if mapping contains keyword that is not - * a TPB parameter. + * @throws SQLException + * if mapping contains keyword that is not a TPB parameter. */ - public static TransactionParameterBuffer processMapping(String mapping) throws FBResourceException { + public static TransactionParameterBuffer processMapping(String mapping) throws SQLException { // TODO instance creation should be delegated to FbDatabase TransactionParameterBuffer result = new TransactionParameterBufferImpl(); @@ -324,13 +323,15 @@ public static TransactionParameterBuffer processMapping(String mapping) throws F try { argValue = Integer.valueOf(parts[1]); } catch (NumberFormatException ex) { - throw new FBResourceException(parts[1] + " is not valid integer value"); + // TODO More specific exception, Jaybird error code + throw new SQLException(parts[1] + " is not valid integer value"); } token = parts[0]; } Integer value = ParameterBufferHelper.getTpbParam(token); if (value == null) { - throw new FBResourceException("Keyword " + token + " unknown. Please check your mapping."); + // TODO More specific exception, Jaybird error code + throw new SQLException("Keyword " + token + " unknown. Please check your mapping."); } if (argValue == null) { @@ -365,6 +366,7 @@ public TransactionParameterBuffer getMapping(int transactionIsolation) { case Connection.TRANSACTION_NONE: default: + // TODO Throw SQLException instead? throw new IllegalArgumentException( "Transaction isolation level " + transactionIsolation + " is not supported."); } @@ -391,6 +393,7 @@ public void setMapping(int transactionIsolation, TransactionParameterBuffer tpb) case Connection.TRANSACTION_READ_UNCOMMITTED: case Connection.TRANSACTION_NONE: default: + // TODO Throw SQLException instead? throw new IllegalArgumentException( "Transaction isolation level " + transactionIsolation + " is not supported."); } diff --git a/src/main/org/firebirdsql/jdbc/FirebirdConnection.java b/src/main/org/firebirdsql/jdbc/FirebirdConnection.java index e6c667d27c..15eb81dbab 100644 --- a/src/main/org/firebirdsql/jdbc/FirebirdConnection.java +++ b/src/main/org/firebirdsql/jdbc/FirebirdConnection.java @@ -2,138 +2,136 @@ * Firebird Open Source JavaEE connector - JDBC driver, public Firebird-specific * JDBC extensions. * - * Redistribution and use in source and binary forms, with or without + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, + * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.firebirdsql.jdbc; -import java.sql.Blob; -import java.sql.Connection; -import java.sql.SQLException; - import org.firebirdsql.gds.ISCConstants; import org.firebirdsql.gds.TransactionParameterBuffer; import org.firebirdsql.gds.ng.FbDatabase; +import java.sql.Blob; +import java.sql.Connection; +import java.sql.SQLException; + /** - * Extension of {@link Connection} interface providing access to Firebird - * specific features. - * + * Extension of {@link Connection} interface providing access to Firebird specific features. + * * @author Roman Rokytskyy */ public interface FirebirdConnection extends Connection { - + int TPB_READ_COMMITTED = ISCConstants.isc_tpb_read_committed; int TPB_CONCURRENCY = ISCConstants.isc_tpb_concurrency; int TPB_CONSISTENCY = ISCConstants.isc_tpb_consistency; - + int TPB_READ = ISCConstants.isc_tpb_read; int TPB_WRITE = ISCConstants.isc_tpb_write; - + int TPB_WAIT = ISCConstants.isc_tpb_wait; int TPB_NOWAIT = ISCConstants.isc_tpb_nowait; - + int TPB_REC_VERSION = ISCConstants.isc_tpb_rec_version; int TPB_NO_REC_VERSION = ISCConstants.isc_tpb_no_rec_version; - + /** * {@inheritDoc} - * + * * @return instance of {@link FirebirdBlob}. */ Blob createBlob() throws SQLException; - + /** * Get current ISC encoding. - * + * * @return current ISC encoding. */ String getIscEncoding() throws SQLException; - + /** - * Set transaction parameters for the specified isolation level. They will + * Set transaction parameters for the specified isolation level. They will * take effect only on the newly started transaction. - * - * @param isolationLevel JDBC isolation level. - * @param parameters array of TPB parameters, see all TPB_* constants. - * - * @throws SQLException if specified transaction parameters cannot be set. - * - * @deprecated use {@link #setTransactionParameters(int, TransactionParameterBuffer)} - * instead. + * + * @param isolationLevel + * JDBC isolation level. + * @param parameters + * array of TPB parameters, see all TPB_* constants. + * @throws SQLException + * if specified transaction parameters cannot be set. + * @deprecated use {@link #setTransactionParameters(int, TransactionParameterBuffer)} instead. */ @Deprecated - void setTransactionParameters(int isolationLevel, int[] parameters) - throws SQLException; - + void setTransactionParameters(int isolationLevel, int[] parameters) throws SQLException; + /** * Get transaction parameters for the specified transaction isolation level. - * - * @param isolationLevel isolation level defined in the {@link Connection} - * interface. - * - * @return instance of {@link TransactionParameterBuffer} containing current - * transaction parameters. - * - * @throws SQLException if error occured obtaining transaction parameters. + * + * @param isolationLevel + * isolation level defined in the {@link Connection} interface. + * @return instance of {@link TransactionParameterBuffer} containing current transaction parameters. + * @throws SQLException + * if error occurred obtaining transaction parameters. */ TransactionParameterBuffer getTransactionParameters(int isolationLevel) throws SQLException; - + /** * Create new instance of {@link TransactionParameterBuffer}. - * + * * @return empty instance of {@link TransactionParameterBuffer}. - * - * @throws SQLException if error occured during this operation. + * @throws SQLException + * if error occurred during this operation. */ TransactionParameterBuffer createTransactionParameterBuffer() throws SQLException; - + /** * Set transaction parameters for the specified transaction isolation level. - * This method replaces the default TPB mapping with the specified one, + *

+ * 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. + *

+ * + * @param tpb + * instance of {@link TransactionParameterBuffer} with parameters + * to set. + * @throws SQLException + * if error occurred during this operation. */ - void setTransactionParameters(int isolationLevel, TransactionParameterBuffer tpb) - throws SQLException; - + void setTransactionParameters(int isolationLevel, TransactionParameterBuffer tpb) throws SQLException; + /** - * Set transaction parameters for the next transactions. This method does - * not change the TPB mapping, but replaces the mapping for the current - * transaction isolation until {@link Connection#setTransactionIsolation(int)} - * is called. + * Set transaction parameters for the next transactions. *

- * 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. + *

+ * + * @param tpb + * instance of {@link TransactionParameterBuffer} with new + * transaction parameters. + * @throws SQLException + * if method is called within a transaction. */ void setTransactionParameters(TransactionParameterBuffer tpb) throws SQLException; diff --git a/src/main/org/firebirdsql/jdbc/FirebirdDriver.java b/src/main/org/firebirdsql/jdbc/FirebirdDriver.java index 6355be5bce..16a649a5c2 100644 --- a/src/main/org/firebirdsql/jdbc/FirebirdDriver.java +++ b/src/main/org/firebirdsql/jdbc/FirebirdDriver.java @@ -1,26 +1,26 @@ /* - * Firebird Open Source J2ee connector - jdbc driver, public Firebird-specific + * Firebird Open Source J2ee connector - jdbc driver, public Firebird-specific * JDBC extensions. * - * Redistribution and use in source and binary forms, with or without + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, + * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.firebirdsql.jdbc; @@ -29,31 +29,30 @@ import java.sql.SQLException; /** - * Extension of the {@link java.sql.Driver} providing methods to set the + * Extension of the {@link java.sql.Driver} providing methods to set the * connection properties programmatically. */ public interface FirebirdDriver extends Driver { /** - * Create new instance of {@link FirebirdConnectionProperties} that can + * Create new instance of {@link FirebirdConnectionProperties} that can * later be used in {@link #connect(FirebirdConnectionProperties)} call. - * + * * @return instance of {@link FirebirdConnectionProperties}. */ FirebirdConnectionProperties newConnectionProperties(); - + /** - * Connect to the specified database using the specified connection + * Connect to the specified database using the specified connection * properties. - * - * @param properties instance of {@link FirebirdConnectionProperties} - * created in {@link #newConnectionProperties()} method. - * + * + * @param properties + * instance of {@link FirebirdConnectionProperties} created in {@link #newConnectionProperties()} method. * @return new connection to the Firebird database. - * - * @throws SQLException if an error happened while connecting to the - * database. + * @throws SQLException + * if an error happened while connecting to the database. + * @deprecated */ - FirebirdConnection connect(FirebirdConnectionProperties properties) - throws SQLException; + FirebirdConnection connect(FirebirdConnectionProperties properties) + throws SQLException; } diff --git a/src/main/org/firebirdsql/jdbc/InternalTransactionCoordinator.java b/src/main/org/firebirdsql/jdbc/InternalTransactionCoordinator.java index f903a651fe..7b8b75310e 100644 --- a/src/main/org/firebirdsql/jdbc/InternalTransactionCoordinator.java +++ b/src/main/org/firebirdsql/jdbc/InternalTransactionCoordinator.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,11 +20,10 @@ import org.firebirdsql.gds.TransactionParameterBuffer; import org.firebirdsql.gds.ng.StatementType; +import org.firebirdsql.jaybird.xca.FBLocalTransaction; import org.firebirdsql.jaybird.xca.FBManagedConnection; -import org.firebirdsql.jaybird.xca.FirebirdLocalTransaction; import org.firebirdsql.util.SQLExceptionChainBuilder; -import javax.resource.ResourceException; import java.sql.SQLException; import java.util.*; @@ -200,12 +199,12 @@ private void setCoordinator(AbstractTransactionCoordinator coordinator) throws S public abstract static class AbstractTransactionCoordinator implements FBObjectListener.StatementListener, FBObjectListener.BlobListener { - protected final FirebirdLocalTransaction localTransaction; + protected final FBLocalTransaction localTransaction; protected final FBConnection connection; protected final Collection statements = new HashSet<>(); - protected AbstractTransactionCoordinator(FBConnection connection, FirebirdLocalTransaction localTransaction) { + protected AbstractTransactionCoordinator(FBConnection connection, FBLocalTransaction localTransaction) { this.localTransaction = localTransaction; this.connection = connection; } @@ -253,37 +252,21 @@ protected void completeStatements(CompletionReason reason) throws SQLException { } final void internalCommit() throws SQLException { - try { - if (localTransaction != null && localTransaction.inTransaction()) { - localTransaction.commit(); - } - } catch (ResourceException ex) { - throw new FBSQLException(ex); + if (localTransaction != null && localTransaction.inTransaction()) { + localTransaction.commit(); } } final void internalRollback() throws SQLException { - try { - if (localTransaction != null && localTransaction.inTransaction()) { - localTransaction.rollback(); - } - } catch (ResourceException ex) { - throw new FBSQLException(ex); + if (localTransaction != null && localTransaction.inTransaction()) { + localTransaction.rollback(); } } public void ensureTransaction() throws SQLException { configureFirebirdAutoCommit(); - - try { - if (!localTransaction.inTransaction()) { - localTransaction.begin(); - } - } catch (ResourceException ex) { - if (ex.getCause() instanceof SQLException) { - throw (SQLException) ex.getCause(); - } - throw new FBSQLException(ex); + if (!localTransaction.inTransaction()) { + localTransaction.begin(); } } @@ -314,8 +297,7 @@ boolean isAutoCommit() throws SQLException { static class AutoCommitCoordinator extends AbstractTransactionCoordinator { - public AutoCommitCoordinator(FBConnection connection, - FirebirdLocalTransaction localTransaction) { + public AutoCommitCoordinator(FBConnection connection, FBLocalTransaction localTransaction) { super(connection, localTransaction); } @@ -377,15 +359,14 @@ public void statementCompleted(FBStatement stmt, boolean success) throws SQLExce } else { localTransaction.rollback(); } - } catch (ResourceException ex) { - SQLException sqlException = new FBSQLException(ex); + } catch (SQLException ex) { try { internalRollback(); } catch (SQLException ex2) { - sqlException.setNextException(ex2); + ex.setNextException(ex2); } - throw sqlException; + throw ex; } } @@ -411,16 +392,12 @@ public void rollback() throws SQLException { @Override void handleConnectionClose() throws SQLException { - try { - if (localTransaction.inTransaction()) { - try { - completeStatements(CompletionReason.COMMIT); - } finally { - internalCommit(); - } + if (localTransaction.inTransaction()) { + try { + completeStatements(CompletionReason.COMMIT); + } finally { + internalCommit(); } - } catch (ResourceException e) { - throw new FBSQLException(e); } } @@ -432,7 +409,7 @@ boolean isAutoCommit() { static class LocalTransactionCoordinator extends AbstractTransactionCoordinator { - public LocalTransactionCoordinator(FBConnection connection, FirebirdLocalTransaction localTransaction) { + public LocalTransactionCoordinator(FBConnection connection, FBLocalTransaction localTransaction) { super(connection, localTransaction); } @@ -456,12 +433,8 @@ public void rollback() throws SQLException { @Override void handleConnectionClose() throws SQLException { - try { - if (localTransaction.inTransaction()) { - rollback(); - } - } catch (ResourceException e) { - throw new FBSQLException(e); + if (localTransaction.inTransaction()) { + rollback(); } } @@ -502,8 +475,7 @@ public void executionStarted(FirebirdBlob blob) throws SQLException { static class FirebirdAutoCommitCoordinator extends LocalTransactionCoordinator { - public FirebirdAutoCommitCoordinator(FBConnection connection, - FirebirdLocalTransaction localTransaction) { + public FirebirdAutoCommitCoordinator(FBConnection connection, FBLocalTransaction localTransaction) { super(connection, localTransaction); } @@ -558,15 +530,14 @@ public void statementCompleted(FBStatement stmt, boolean success) throws SQLExce } else { localTransaction.rollback(); } - } catch (ResourceException ex) { - SQLException sqlException = new FBSQLException(ex); + } catch (SQLException ex) { try { internalRollback(); } catch (SQLException ex2) { - sqlException.setNextException(ex2); + ex.setNextException(ex2); } - throw sqlException; + throw ex; } } } @@ -583,16 +554,12 @@ public void rollback() throws SQLException { @Override void handleConnectionClose() throws SQLException { - try { - if (localTransaction.inTransaction()) { - try { - completeStatements(CompletionReason.COMMIT); - } finally { - internalCommit(); - } + if (localTransaction.inTransaction()) { + try { + completeStatements(CompletionReason.COMMIT); + } finally { + internalCommit(); } - } catch (ResourceException e) { - throw new FBSQLException(e); } } diff --git a/src/resources/META-INF/ra.xml b/src/resources/META-INF/ra.xml deleted file mode 100644 index 98ef6bbe23..0000000000 --- a/src/resources/META-INF/ra.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - Firebird Database Connector - Firebird Database - 1.0 - Relational Database - 1.0 - - org.firebirdsql.jaybird.xca.FBManagedConnectionFactory - javax.sql.DataSource - org.firebirdsql.jdbc.FBDataSource - java.sql.Connection - org.firebirdsql.jdbc.FBConnection - XATransaction - - - Type - java.lang.String - - - - - Database - java.lang.String - - - - UserName - java.lang.String - - - - Password - java.lang.String - - - - RoleName - java.lang.String - - - - Encoding - java.lang.String - - - - SqlDialect - int - - - - - - - BlobBufferSize - int - - - - TransactionIsolationName - java.lang.String - TRANSACTION_READ_COMMITTED - - - - - - BasicPassword - javax.resource.spi.security.PasswordCredential - - - false - - diff --git a/src/stylesheets/junit-frames.xsl b/src/stylesheets/junit-frames.xsl index 53529407c0..ca0871bded 100644 --- a/src/stylesheets/junit-frames.xsl +++ b/src/stylesheets/junit-frames.xsl @@ -43,7 +43,7 @@ --> -Firebirdsql pure java jca-jdbc driver Test Suite Results +Firebirdsql pure java jdbc driver Test Suite Results diff --git a/src/test/org/firebirdsql/common/FBTestProperties.java b/src/test/org/firebirdsql/common/FBTestProperties.java index b718c1db31..477ce0c7b8 100644 --- a/src/test/org/firebirdsql/common/FBTestProperties.java +++ b/src/test/org/firebirdsql/common/FBTestProperties.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 @@ -22,14 +22,12 @@ import org.firebirdsql.gds.impl.GDSType; import org.firebirdsql.gds.ng.FbDatabaseFactory; import org.firebirdsql.jaybird.xca.FBManagedConnectionFactory; -import org.firebirdsql.jaybird.xca.InternalConnectionManager; import org.firebirdsql.jdbc.FBDriver; import org.firebirdsql.jdbc.FirebirdConnection; import org.firebirdsql.management.FBManager; import org.firebirdsql.management.FBServiceManager; import org.firebirdsql.util.FirebirdSupportInfo; -import javax.resource.spi.ConnectionManager; import java.io.File; import java.sql.DriverManager; import java.sql.SQLException; @@ -51,7 +49,7 @@ public final class FBTestProperties { } } - private static ResourceBundle testDefaults = ResourceBundle.getBundle("unit_test_defaults"); + private static final ResourceBundle testDefaults = ResourceBundle.getBundle("unit_test_defaults"); public static String getProperty(String property) { return getProperty(property, null); @@ -208,13 +206,6 @@ public static FBManagedConnectionFactory createFBManagedConnectionFactory() { return new FBManagedConnectionFactory(getGdsType()); } - public static FBManagedConnectionFactory createFBManagedConnectionFactory( - @SuppressWarnings("UnusedParameters") ConnectionManager cm) { - FBManagedConnectionFactory mcf = new FBManagedConnectionFactory(getGdsType()); - mcf.setDefaultConnectionManager(new InternalConnectionManager()); - return mcf; - } - public static FBManager createFBManager() { return new FBManager(getGdsType()); } diff --git a/src/test/org/firebirdsql/ds/TestDataSourceFactory.java b/src/test/org/firebirdsql/ds/TestDataSourceFactory.java index 0204a61533..4ddf742a04 100644 --- a/src/test/org/firebirdsql/ds/TestDataSourceFactory.java +++ b/src/test/org/firebirdsql/ds/TestDataSourceFactory.java @@ -1,7 +1,5 @@ /* - * $Id$ - * - * Firebird Open Source J2EE 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 @@ -14,7 +12,7 @@ * 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 CVS history command. + * can be obtained from a source control history command. * * All rights reserved. */ @@ -31,7 +29,7 @@ /** * Tests for {@link DataSourceFactory} and - indirectly - the correctness of the getReference() method of - * {@link FBConnectionPoolDataSource} and {@link FBXADataSource}. + * {@link FBConnectionPoolDataSource}, {@link FBXADataSource}, and {@link FBSimpleDataSource}. * * @author Mark Rotteveel * @since 2.2 @@ -50,11 +48,10 @@ public class TestDataSourceFactory { private static final String DESCRIPTION = "Description of originalDS"; /** - * Fills the properties (exposed with JavaBeans setters) of FBAbstractCommonDataSource for testing. Does not set nonStandardProperty - * and encoding (as it is set through charSet). + * Fills the properties (exposed with JavaBeans setters) of FBAbstractCommonDataSource for testing. Does not set + * nonStandardProperty and encoding (as it is set through charSet). * * @param instance Instance to configure - * @throws SQLException */ private void fillFBAbstractCommonDataSourceProperties(FBAbstractCommonDataSource instance) throws SQLException { instance.setDescription(DESCRIPTION); @@ -70,10 +67,10 @@ private void fillFBAbstractCommonDataSourceProperties(FBAbstractCommonDataSource } /** - * Validates if the instance of FBAbstractCommonDataSource has the values as set by {@link TestDataSourceFactory#fillFBAbstractCommonDataSourceProperties(FBAbstractCommonDataSource)}. + * Validates if the instance of FBAbstractCommonDataSource has the values as set by + * {@link TestDataSourceFactory#fillFBAbstractCommonDataSourceProperties(FBAbstractCommonDataSource)}. * * @param instance Instance to validate - * @throws SQLException */ private void assertFBAbstractCommonDataSourceProperties(FBAbstractCommonDataSource instance) throws SQLException{ assertEquals(DESCRIPTION, instance.getDescription()); @@ -99,7 +96,6 @@ private void assertFBAbstractCommonDataSourceProperties(FBAbstractCommonDataSour *
  • If all the properties set on the original are also set on the new instance
  • * *

    - * @throws Exception */ @Test public void testBuildFBConnectionPoolDataSource_basicProperties() throws Exception { @@ -127,7 +123,6 @@ public void testBuildFBConnectionPoolDataSource_basicProperties() throws Excepti *
  • If all the properties set on the original are also set on the new instance
  • * *

    - * @throws Exception */ @Test public void testBuildFBXADataSource_basicProperties() throws Exception { @@ -155,7 +150,6 @@ public void testBuildFBXADataSource_basicProperties() throws Exception { *
  • If an unset property is handled correctly
  • * *

    - * @throws Exception */ @Test public void testBuildFBConnectionPoolDataSource_nonStandardProperties() throws Exception { @@ -185,7 +179,6 @@ public void testBuildFBConnectionPoolDataSource_nonStandardProperties() throws E *
  • If an unset property is handled correctly
  • * *

    - * @throws Exception */ @Test public void testBuildFBXADataSource_nonStandardProperties() throws Exception { @@ -202,4 +195,40 @@ public void testBuildFBXADataSource_nonStandardProperties() throws Exception { assertEquals("madeUpValue", newDS.getNonStandardProperty("madeUpProperty")); assertNull(newDS.getDescription()); } + + @Test + public void testBuildFBSimpleDataSource() throws Exception { + final FBSimpleDataSource originalDS = new FBSimpleDataSource(); + originalDS.setDescription(DESCRIPTION); + originalDS.setType(TYPE); + final String database = String.format("//%s:%d/%s", SERVER_NAME, PORT_NUMBER, DATABASE_NAME); + originalDS.setDatabase(database); + originalDS.setUserName(USER); + originalDS.setPassword(PASSWORD); + originalDS.setEncoding(ENCODING); + originalDS.setLoginTimeout(LOGIN_TIMEOUT); + originalDS.setRoleName(ROLE_NAME); + originalDS.setNonStandardProperty("buffersNumber=127"); // note number of buffers is apparently byte, so using higher values can give weird results + originalDS.setNonStandardProperty("defaultTransactionIsolation", Integer.toString(Connection.TRANSACTION_SERIALIZABLE)); + originalDS.setNonStandardProperty("madeUpProperty", "madeUpValue"); + Reference ref = originalDS.getReference(); + + assertEquals("Unexpected factory name", DataSourceFactory.class.getName(), ref.getFactoryClassName()); + assertEquals("Unexpected class name", FBSimpleDataSource.class.getName(), ref.getClassName()); + + FBSimpleDataSource newDS = + (FBSimpleDataSource) new DataSourceFactory().getObjectInstance(ref, null, null, null); + + assertEquals(DESCRIPTION, newDS.getDescription()); + assertEquals(TYPE, newDS.getType()); + assertEquals(database, newDS.getDatabase()); + assertEquals(USER, newDS.getUserName()); + assertEquals(PASSWORD, newDS.getPassword()); + assertEquals(ENCODING, newDS.getEncoding()); + assertEquals(LOGIN_TIMEOUT, newDS.getLoginTimeout()); + assertEquals(ROLE_NAME, newDS.getRoleName()); + assertEquals("127", newDS.getNonStandardProperty("buffersNumber")); + assertEquals(Integer.toString(Connection.TRANSACTION_SERIALIZABLE), newDS.getNonStandardProperty("defaultTransactionIsolation")); + assertEquals("madeUpValue", newDS.getNonStandardProperty("madeUpProperty")); + } } diff --git a/src/test/org/firebirdsql/ds/TestFBXADataSource.java b/src/test/org/firebirdsql/ds/TestFBXADataSource.java index ca67ce6bfa..a4754eaee8 100644 --- a/src/test/org/firebirdsql/ds/TestFBXADataSource.java +++ b/src/test/org/firebirdsql/ds/TestFBXADataSource.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 @@ -47,8 +47,7 @@ import static org.junit.Assume.assumeTrue; /** - * Test for XADataSource. Note behavior of XAResource (ManagedConnection) is tested in - * {@link TestFBXAResource}. + * Test for XADataSource. Note behavior of XAResource (FBManagedConnection) is tested in {@link TestFBXAResource}. * * @author Mark Rotteveel * @since 2.2 diff --git a/src/test/org/firebirdsql/jaybird/xca/InternalConnectionManager.java b/src/test/org/firebirdsql/jaybird/xca/InternalConnectionManager.java deleted file mode 100644 index cc4fd0bddf..0000000000 --- a/src/test/org/firebirdsql/jaybird/xca/InternalConnectionManager.java +++ /dev/null @@ -1,69 +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.ResourceException; -import javax.resource.spi.*; -import java.io.PrintWriter; -import java.io.Serializable; - -public class InternalConnectionManager implements ConnectionManager, - ConnectionEventListener, Serializable { - - public InternalConnectionManager() { - super(); - } - - public Object allocateConnection(ManagedConnectionFactory mcf, - ConnectionRequestInfo cxRequestInfo) throws ResourceException { - - FBManagedConnection mc = (FBManagedConnection) mcf.createManagedConnection(null, cxRequestInfo); - mc.setManagedEnvironment(true); - mc.setConnectionSharing(true); - mc.addConnectionEventListener(this); - return mc.getConnection(null, cxRequestInfo); - } - - public void connectionClosed(ConnectionEvent event) { - PrintWriter externalLog = ((FBManagedConnection) event.getSource()).getLogWriter(); - try { - ((FBManagedConnection) event.getSource()).destroy(event); - } catch (ResourceException e) { - if (externalLog != null) externalLog.println("Exception closing unmanaged connection: " + e); - } - } - - public void localTransactionStarted(ConnectionEvent event) { - } - - public void localTransactionCommitted(ConnectionEvent event) { - } - - public void localTransactionRolledback(ConnectionEvent event) { - } - - public void connectionErrorOccurred(ConnectionEvent event) { - PrintWriter externalLog = ((FBManagedConnection) event.getSource()).getLogWriter(); - try { - ((FBManagedConnection) event.getSource()).destroy(event); - } catch (ResourceException e) { - if (externalLog != null) externalLog.println("Exception closing unmanaged connection: " + e); - } - } -} diff --git a/src/test/org/firebirdsql/jaybird/xca/TestFBBlob.java b/src/test/org/firebirdsql/jaybird/xca/TestFBBlob.java index 64775a9768..645c1557ab 100644 --- a/src/test/org/firebirdsql/jaybird/xca/TestFBBlob.java +++ b/src/test/org/firebirdsql/jaybird/xca/TestFBBlob.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 @@ -22,7 +22,6 @@ import org.junit.After; import org.junit.Test; -import javax.resource.spi.LocalTransaction; import javax.sql.DataSource; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -35,23 +34,17 @@ import static org.junit.Assert.assertEquals; -/** - * Describe class 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 the hostName 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 map = new HashMap<>(); map.put("special", "isc_tpb_concurrency,isc_tpb_write,isc_tpb_wait,isc_tpb_lock_timeout=5"); - expectedException.expect(FBResourceException.class); + expectedException.expect(SQLException.class); new FBTpbMapper(map); } @@ -137,17 +137,17 @@ public void testProcessMappingWithLockTimeout() throws Exception { } @Test - public void testProcessMapping_TokenIsNotATpbArgument_throwsFBResourceException() throws Exception { + public void testProcessMapping_TokenIsNotATpbArgument_throwsSQLException() throws Exception { String testMapping = "not_a_tpb_argument"; - expectedException.expect(FBResourceException.class); + expectedException.expect(SQLException.class); FBTpbMapper.processMapping(testMapping); } @Test - public void testProcessMapping_ValueIsNotAnInteger_throwsFBResourceException() throws Exception { + public void testProcessMapping_ValueIsNotAnInteger_throwsSQLException() throws Exception { String testMapping = "isc_tpb_lock_timeout=x"; - expectedException.expect(FBResourceException.class); + expectedException.expect(SQLException.class); FBTpbMapper.processMapping(testMapping); }