
org.firebirdsql.gds.ng.jna.JnaTransaction Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaybird-native Show documentation
Show all versions of jaybird-native Show documentation
Native GDS Factory Plugin for Jaybird JDBC Driver
The newest version!
/*
* 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.gds.ng.jna;
import com.sun.jna.ptr.IntByReference;
import org.firebirdsql.gds.ng.AbstractFbTransaction;
import org.firebirdsql.gds.ng.FbDatabase;
import org.firebirdsql.gds.ng.LockCloseable;
import org.firebirdsql.gds.ng.TransactionState;
import org.firebirdsql.gds.ng.listeners.DatabaseListener;
import org.firebirdsql.jaybird.util.Cleaners;
import org.firebirdsql.jna.fbclient.FbClientLibrary;
import org.firebirdsql.jna.fbclient.ISC_STATUS;
import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import java.sql.SQLException;
/**
* Implementation of {@link org.firebirdsql.gds.ng.FbTransaction} for native client access.
*
* @author Mark Rotteveel
* @since 3.0
*/
public class JnaTransaction extends AbstractFbTransaction {
private static final System.Logger log = System.getLogger(JnaTransaction.class.getName());
private final IntByReference handle;
private final ISC_STATUS[] statusVector = new ISC_STATUS[JnaDatabase.STATUS_VECTOR_SIZE];
private final FbClientLibrary clientLibrary;
private final Cleaner.Cleanable cleanable;
/**
* Initializes AbstractFbTransaction.
*
* @param database
* FbDatabase that created this handle.
* @param transactionHandle
* Transaction handle
* @param initialState
* Initial transaction state (allowed values are {@link org.firebirdsql.gds.ng.TransactionState#ACTIVE}
* and {@link org.firebirdsql.gds.ng.TransactionState#PREPARED})
*/
public JnaTransaction(JnaDatabase database, IntByReference transactionHandle, TransactionState initialState) {
super(initialState, database);
handle = transactionHandle;
clientLibrary = database.getClientLibrary();
cleanable = Cleaners.getJbCleaner().register(this, new CleanupAction(transactionHandle, database));
}
@Override
public JnaDatabase getDatabase() {
return (JnaDatabase) super.getDatabase();
}
@Override
public int getHandle() {
return handle.getValue();
}
public IntByReference getJnaHandle() {
return handle;
}
@Override
public void commit() throws SQLException {
try (LockCloseable ignored = withLock()) {
final JnaDatabase db = getDatabase();
db.checkConnected();
switchState(TransactionState.COMMITTING);
clientLibrary.isc_commit_transaction(statusVector, handle);
processStatusVector();
switchState(TransactionState.COMMITTED);
cleanable.clean();
} catch (SQLException e) {
exceptionListenerDispatcher.errorOccurred(e);
throw e;
} finally {
logUnexpectedState(TransactionState.COMMITTED, log);
}
}
@Override
public void rollback() throws SQLException {
try (LockCloseable ignored = withLock()) {
final JnaDatabase db = getDatabase();
db.checkConnected();
switchState(TransactionState.ROLLING_BACK);
clientLibrary.isc_rollback_transaction(statusVector, handle);
processStatusVector();
switchState(TransactionState.ROLLED_BACK);
cleanable.clean();
} catch (SQLException e) {
exceptionListenerDispatcher.errorOccurred(e);
throw e;
} finally {
logUnexpectedState(TransactionState.ROLLED_BACK, log);
}
}
@Override
public void prepare(byte[] recoveryInformation) throws SQLException {
boolean noRecoveryInfo = recoveryInformation == null || recoveryInformation.length == 0;
try (LockCloseable ignored = withLock()) {
final JnaDatabase db = getDatabase();
db.checkConnected();
switchState(TransactionState.PREPARING);
if (noRecoveryInfo) {
clientLibrary.isc_prepare_transaction(statusVector, handle);
} else {
clientLibrary.isc_prepare_transaction2(statusVector, handle, (short) recoveryInformation.length,
recoveryInformation);
}
processStatusVector();
switchState(TransactionState.PREPARED);
} catch (SQLException e) {
exceptionListenerDispatcher.errorOccurred(e);
throw e;
} finally {
logUnexpectedState(TransactionState.PREPARED, log);
}
}
@Override
public byte[] getTransactionInfo(byte[] requestItems, int maxBufferLength) throws SQLException {
try {
final ByteBuffer responseBuffer = ByteBuffer.allocateDirect(maxBufferLength);
try (LockCloseable ignored = withLock()) {
final JnaDatabase db = getDatabase();
db.checkConnected();
clientLibrary.isc_transaction_info(statusVector, handle, (short) requestItems.length, requestItems,
(short) maxBufferLength, responseBuffer);
processStatusVector();
}
final byte[] responseArray = new byte[maxBufferLength];
responseBuffer.get(responseArray);
return responseArray;
} catch (SQLException e) {
exceptionListenerDispatcher.errorOccurred(e);
throw e;
}
}
private void processStatusVector() throws SQLException {
getDatabase().processStatusVector(statusVector, null);
}
private static final class CleanupAction implements Runnable, DatabaseListener {
private final IntByReference handle;
@SuppressWarnings("java:S3077")
private volatile JnaDatabase database;
private CleanupAction(IntByReference handle, JnaDatabase database) {
this.handle = handle;
this.database = database;
database.addWeakDatabaseListener(this);
}
@Override
public void detaching(FbDatabase database) {
this.database = null;
database.removeDatabaseListener(this);
}
@Override
public void run() {
final JnaDatabase database = this.database;
if (database == null) return;
detaching(database);
if (handle.getValue() == 0
|| !database.hasFeature(FbClientFeature.FB_DISCONNECT_TRANSACTION)
|| !database.isAttached()) return;
/*
ACTIVE transactions are held in AbstractFbDatabase.activeTransactions, so such cleanup would only
happen when the connection itself was also GC'd, which means an attempt to roll back would likely fail
anyway (and the server will perform a rollback eventually), so we don't perform any action other than
fb_disconnect_transaction
*/
database.getClientLibrary()
.fb_disconnect_transaction(new ISC_STATUS[JnaDatabase.STATUS_VECTOR_SIZE], handle);
// We intentionally ignore the status vector result
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy