Skip to content

Commit

Permalink
Make Transactional driver pool configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
graben committed Jan 4, 2025
1 parent ed0b3f9 commit 1ea0df9
Show file tree
Hide file tree
Showing 16 changed files with 510 additions and 39 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ public class Application {

By default, [Narayana Transactional driver](https://www.narayana.io/docs/api/com/arjuna/ats/jdbc/TransactionalDriver.html)
is used to enlist a relational database to a JTA transaction which provides a basic XAResource enlistment and recovery as
well as a simple pooling mechanism.
well as a simple pooling mechanism which is disabled as default. See [TransactionalDriverProperties](narayana-spring-boot-core/src/main/java/dev/snowdrop/boot/narayana/core/properties/TransactionalDriverProperties.java)
for more details.

> Be aware that Narayana Transactional driver automatically set transaction isolation level to `Connection.TRANSACTION_SERIALIZABLE`,
which might change default behaviour of the used database system!
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright 2020 Red Hat, Inc, and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.arjuna.ats.internal.jdbc;

import java.sql.SQLException;

import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.xa.XAResource;

import jakarta.transaction.Transaction;

import com.arjuna.ats.internal.jdbc.drivers.modifiers.ConnectionModifier;
import com.arjuna.ats.jdbc.logging.jdbcLogger;
import dev.snowdrop.boot.narayana.core.jdbc.NamedXAResource;

public class ProvidedXADataSourceConnection implements ConnectionControl, TransactionalDriverXAConnection {

private final BaseTransactionalDriverXAConnection delegate = new BaseTransactionalDriverXAConnection() {
};

public ProvidedXADataSourceConnection(String dbName, String user, String passwd, XADataSource xaDatasource, ConnectionImple conn) {
if (jdbcLogger.logger.isTraceEnabled()) {
jdbcLogger.logger.trace("ProvidedXADataSourceConnection.ProvidedXADataSourceConnection( " + dbName + ", " + user + ", " + passwd + ", " + xaDatasource + " )");
}
this.delegate._dbName = dbName;
this.delegate._user = user;
this.delegate._passwd = passwd;
this.delegate._theDataSource = xaDatasource;
this.delegate._theArjunaConnection = conn;
}

@Override
public String dynamicClass() {
return this.delegate.dynamicClass();
}

@Override
public String dataSourceName() {
return this.delegate.dataSourceName();
}

@Override
public String password() {
return this.delegate.password();
}

@Override
public void setModifier(ConnectionModifier cm) {
this.delegate.setModifier(cm);
}

@Override
public Transaction transaction() {
return this.delegate.transaction();
}

@Override
public String url() {
return this.delegate.url();
}

@Override
public String user() {
return this.delegate.user();
}

@Override
public XADataSource xaDataSource() {
return this.delegate.xaDataSource();
}

@Override
public void closeCloseCurrentConnection() throws SQLException {
this.delegate.closeCloseCurrentConnection();
}

@Override
public XAConnection getConnection() throws SQLException {
return this.delegate.getConnection();
}

@Override
public XAResource getResource() throws SQLException {
if (this.delegate._theXAResource == null) {
this.delegate._theXAResource = new NamedXAResource(this.delegate.getResource(), this.delegate.dataSourceName());
}
return this.delegate._theXAResource;
}

@Override
public boolean inuse() {
return this.delegate.inuse();
}

@Override
public boolean setTransaction(Transaction tx) {
return this.delegate.setTransaction(tx);
}

@Override
public boolean validTransaction(Transaction tx) {
return this.delegate.validTransaction(tx);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@

package dev.snowdrop.boot.narayana.core.jdbc;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;

import javax.sql.DataSource;
import javax.sql.XADataSource;

import com.arjuna.ats.internal.jdbc.drivers.modifiers.IsSameRMModifier;
import com.arjuna.ats.internal.jdbc.drivers.modifiers.ModifierFactory;
import com.arjuna.ats.internal.jdbc.drivers.modifiers.SupportsMultipleConnectionsModifier;
import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;
import com.arjuna.ats.jta.recovery.XAResourceRecoveryHelper;
import dev.snowdrop.boot.narayana.core.properties.RecoveryCredentialsProperties;
import dev.snowdrop.boot.narayana.core.properties.TransactionalDriverProperties;
import org.springframework.boot.jdbc.XADataSourceWrapper;

/**
Expand All @@ -33,14 +41,18 @@
public abstract class AbstractXADataSourceWrapper implements XADataSourceWrapper {

private final XARecoveryModule xaRecoveryModule;
protected final TransactionalDriverProperties transactionalDriverProperties;
private final RecoveryCredentialsProperties recoveryCredentials;

protected AbstractXADataSourceWrapper(XARecoveryModule xaRecoveryModule, RecoveryCredentialsProperties recoveryCredentials) {
protected AbstractXADataSourceWrapper(XARecoveryModule xaRecoveryModule,
TransactionalDriverProperties transactionalDriverProperties,
RecoveryCredentialsProperties recoveryCredentials) {
this.xaRecoveryModule = xaRecoveryModule;
this.transactionalDriverProperties = transactionalDriverProperties;
this.recoveryCredentials = recoveryCredentials;
}

protected abstract DataSource wrapDataSourceInternal(XADataSource dataSource) throws Exception;
protected abstract DataSource wrapDataSourceInternal(XADataSource dataSource);

/**
* Register newly created recovery helper with the {@link XARecoveryModule} and delegate data source wrapping.
Expand All @@ -53,15 +65,34 @@ protected AbstractXADataSourceWrapper(XARecoveryModule xaRecoveryModule, Recover
public DataSource wrapDataSource(XADataSource dataSource) throws Exception {
XAResourceRecoveryHelper recoveryHelper = getRecoveryHelper(dataSource);
this.xaRecoveryModule.addXAResourceRecoveryHelper(recoveryHelper);
registerModifier(dataSource);
return wrapDataSourceInternal(dataSource);
}

private XAResourceRecoveryHelper getRecoveryHelper(XADataSource dataSource) {
if (this.recoveryCredentials.isValid()) {
return new DataSourceXAResourceRecoveryHelper(dataSource, this.recoveryCredentials.getUser(),
this.recoveryCredentials.getPassword());
this.recoveryCredentials.getPassword(), this.transactionalDriverProperties.getName());
}
return new DataSourceXAResourceRecoveryHelper(dataSource);
return new DataSourceXAResourceRecoveryHelper(dataSource, this.transactionalDriverProperties.getName());
}

private void registerModifier(XADataSource dataSource) throws SQLException {
try (Connection conn = dataSource.getXAConnection().getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
String name = metaData.getDatabaseProductName();
String driver = metaData.getDriverName();
int major = metaData.getDriverMajorVersion();
int minor = metaData.getDriverMinorVersion();
if (name.startsWith("DB2")
|| name.equals("H2")
|| name.equals("Microsoft SQL Server")
|| name.equals("MySQL")
|| name.equals("Oracle")) {
ModifierFactory.putModifier(driver, major, minor, IsSameRMModifier.class.getName());
} else if (name.equals("PostgreSQL")) {
ModifierFactory.putModifier(driver, major, minor, SupportsMultipleConnectionsModifier.class.getName());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@
public class DataSourceXAResourceRecoveryHelper implements XAResourceRecoveryHelper, XAResource {

private final ConnectionManager connectionManager;
private final String name;

/**
* Create a new {@link DataSourceXAResourceRecoveryHelper} instance.
*
* @param xaDataSource the XA data source
* @param name the datasource name or {@code null}
*/
public DataSourceXAResourceRecoveryHelper(XADataSource xaDataSource) {
this(xaDataSource, null, null);
public DataSourceXAResourceRecoveryHelper(XADataSource xaDataSource, String name) {
this(xaDataSource, null, null, name);
}

/**
Expand All @@ -47,9 +49,11 @@ public DataSourceXAResourceRecoveryHelper(XADataSource xaDataSource) {
* @param xaDataSource the XA data source
* @param user the database user or {@code null}
* @param password the database password or {@code null}
* @param name the datasource name or {@code null}
*/
public DataSourceXAResourceRecoveryHelper(XADataSource xaDataSource, String user, String password) {
public DataSourceXAResourceRecoveryHelper(XADataSource xaDataSource, String user, String password, String name) {
this.connectionManager = new ConnectionManager(xaDataSource, user, password);
this.name = name;
}

@Override
Expand All @@ -67,7 +71,7 @@ public XAResource[] getXAResources() {
}
}

return new XAResource[]{this};
return new XAResource[]{new NamedXAResource(this, this.name)};
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;
import dev.snowdrop.boot.narayana.core.properties.RecoveryCredentialsProperties;
import dev.snowdrop.boot.narayana.core.properties.TransactionalDriverProperties;

/**
* {@link AbstractXADataSourceWrapper} implementation that uses {@link NarayanaDataSource} to wrap an
Expand All @@ -33,7 +34,7 @@ public class GenericXADataSourceWrapper extends AbstractXADataSourceWrapper {
/**
* Create a new {@link GenericXADataSourceWrapper} instance.
*
* @param xaRecoveryModule recovery module to register data source with.
* @param xaRecoveryModule recovery module to register data source with.
*/
public GenericXADataSourceWrapper(XARecoveryModule xaRecoveryModule) {
this(xaRecoveryModule, RecoveryCredentialsProperties.DEFAULT);
Expand All @@ -42,11 +43,33 @@ public GenericXADataSourceWrapper(XARecoveryModule xaRecoveryModule) {
/**
* Create a new {@link GenericXADataSourceWrapper} instance.
*
* @param xaRecoveryModule recovery module to register data source with.
* @param recoveryCredentials credentials for recovery helper
* @param xaRecoveryModule recovery module to register data source with.
* @param recoveryCredentials credentials for recovery helper
*/
public GenericXADataSourceWrapper(XARecoveryModule xaRecoveryModule, RecoveryCredentialsProperties recoveryCredentials) {
super(xaRecoveryModule, recoveryCredentials);
this(xaRecoveryModule, new TransactionalDriverProperties(), recoveryCredentials);
}

/**
* Create a new {@link GenericXADataSourceWrapper} instance.
*
* @param xaRecoveryModule recovery module to register data source with.
* @param transactionalDriverProperties Transactional driver properties
*/
public GenericXADataSourceWrapper(XARecoveryModule xaRecoveryModule, TransactionalDriverProperties transactionalDriverProperties) {
this(xaRecoveryModule, transactionalDriverProperties, RecoveryCredentialsProperties.DEFAULT);
}

/**
* Create a new {@link GenericXADataSourceWrapper} instance.
*
* @param xaRecoveryModule recovery module to register data source with.
* @param transactionalDriverProperties Transactional driver properties
* @param recoveryCredentials credentials for recovery helper
*/
public GenericXADataSourceWrapper(XARecoveryModule xaRecoveryModule, TransactionalDriverProperties transactionalDriverProperties,
RecoveryCredentialsProperties recoveryCredentials) {
super(xaRecoveryModule, transactionalDriverProperties, recoveryCredentials);
}

/**
Expand All @@ -57,7 +80,7 @@ public GenericXADataSourceWrapper(XARecoveryModule xaRecoveryModule, RecoveryCre
*/
@Override
protected DataSource wrapDataSourceInternal(XADataSource dataSource) {
return new NarayanaDataSource(dataSource);
return new NarayanaDataSource(dataSource, this.transactionalDriverProperties);
}

}
Loading

0 comments on commit 1ea0df9

Please sign in to comment.