org.eclipse.persistence.internal.jpa.transaction.TransactionImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.jpa.transaction;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.util.Vector;
import java.sql.*;
import javax.transaction.xa.XAResource;
import jakarta.transaction.*;
import org.eclipse.persistence.exceptions.TransactionException;
import org.eclipse.persistence.internal.jpa.jdbc.ConnectionProxyHandler;
import org.eclipse.persistence.internal.jpa.jdbc.DataSourceImpl;
/**
* Implementation of JTA Transaction class. The guts of the tx logic
* is contained in this class.
*
* Currently support is limited to enlisting only a single tx data source
*/
@Deprecated
public class TransactionImpl implements Transaction {
// Set by client-induced rollback marking
boolean markedForRollback;
// Used to maintain the tx status
int status;
// Collection of Synchronization listeners
Vector listeners;
// The transactional connection we use
Connection connection;
static Class proxyClass = Proxy.getProxyClass(Connection.class.getClassLoader(), new Class[] { Connection.class });
// The enlisted data source
DataSourceImpl dataSource;
/***** Static constants *****/
// Cribbed from java.transaction.Status
public static final int STATUS_ACTIVE = 0;
public static final int STATUS_MARKED_ROLLBACK = 1;
public static final int STATUS_PREPARED = 2;
public static final int STATUS_COMMITTED = 3;
public static final int STATUS_ROLLEDBACK = 4;
public static final int STATUS_UNKNOWN = 5;
public static final int STATUS_NO_TRANSACTION = 6;
public static final int STATUS_PREPARING = 7;
public static final int STATUS_COMMITTING = 8;
public static final int STATUS_ROLLING_BACK = 9;
// Set this to true for debugging of afterCompletion exceptions
public static boolean DUMP_AFTER_COMPLETION_ERRORS = true;
/************************/
/***** Internal API *****/
/************************/
private void debug(String s) {
System.out.println(s);
}
/*
* Constructor invoked and new instance created on tx begin
*/
public TransactionImpl() {
markedForRollback = false;
status = STATUS_ACTIVE;
listeners = new Vector();
}
/*
* Lazily allocate the connection. This will be used
* by the data source if in a transaction.
*/
public Connection getConnection(DataSourceImpl ds, String user, String password) throws SQLException {
// We don't have a datasource connection yet, so allocate one
if (connection == null) {
debug("TxImpl - allocating new connection");
dataSource = ds;
connection = ds.internalGetConnection(user, password);
connection.setAutoCommit(false);
} else {
// We already have a connection. Make sure the data sources are the same.
if (ds.getName() != dataSource.getName()) {
throw TransactionException.multipleResourceException();
}
}
// return connection;
// Allocate and return a proxy for the connection
debug("TxImpl - creating connection proxy");
Connection proxyConnection = null;
try {
InvocationHandler handler = new ConnectionProxyHandler(connection);
proxyConnection = (Connection)proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });
} catch (Exception ex) {
throw TransactionException.internalProxyException(ex);
}
return proxyConnection;
}
/*
* Invoke afterCompletion callbacks.
* If DUMP_AFTER_COMPLETION_ERRORS flag is set then dump
* the exceptions to System.out, otherwise swallow them.
*
* NOTE: In either case it will not affect the outcome
* of the transaction.
*/
public void invokeAfterCompletion() {
// Call all of the afterCompletion callbacks
debug("TxImpl - invoking afterCompletion");
int i;
int j;
for (i = 0, j = listeners.size(); i < j; i++) {
try {
((Synchronization)listeners.elementAt(i)).afterCompletion(status);
} catch (Throwable t) {
if (DUMP_AFTER_COMPLETION_ERRORS) {
t.printStackTrace(System.out);
}
}
}
}
/*
* Rollback the transaction on the connection.
*/
public void rollbackConnection() throws SQLException {
if (connection != null) {
debug("TxImpl - rolling back connection");
status = STATUS_ROLLING_BACK;
connection.rollback();
status = STATUS_ROLLEDBACK;
}
}
/*
* Commit the transaction on the connection.
*/
public void commitConnection() throws SQLException {
if (connection != null) {
debug("TxImpl - committing connection");
status = STATUS_COMMITTING;
connection.commit();
status = STATUS_COMMITTED;
}
}
/*
* Clean up after everything is over
*/
public void cleanup() {
debug("TxImpl - cleanup");
if (connection != null) {
try {
connection.close();
} catch (Exception ex) {
}
// Ignore
connection = null;
}
status = STATUS_NO_TRANSACTION;
}
/*************************************/
/***** Supported Transaction API *****/
/*************************************/
@Override
public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException {
Exception error = null;
debug("TxImpl - commit");
// Make sure we are allowed to proceed
switch (status) {
case STATUS_ACTIVE:// This is the normal case - do nothing
break;
case STATUS_MARKED_ROLLBACK: {
// Tx was marked for rollback by the user, error
error = new ExceptionFactory().txMarkedForRollbackException();
break;
}
default:// Tx in some other state, error
throw new ExceptionFactory().invalidStateException(status);
}
// Call beforeCompletion callback.
if (error == null) {
try {
debug("TxImpl - invoking beforeCompletion");
int i;
int j;
for (i = 0, j = listeners.size(); i < j; i++) {
((Synchronization)listeners.elementAt(i)).beforeCompletion();
}
} catch (Exception ex) {
error = ex;
status = STATUS_ROLLING_BACK;
debug("TxImpl - error in beforeCompletion: " + ex);
}
}
// Now if we didn't get any errors then commit the connection
if ((error == null) && (status == STATUS_ACTIVE)) {
try {
commitConnection();
} catch (Exception ex) {
error = ex;
}
} else {
try {
rollbackConnection();
} catch (Exception ex) {
error = ex;
}
}
// Whether we were successful or not, call afterCompletion and clean up
invokeAfterCompletion();
cleanup();
// Throw any error that may have occurred at any point in the commit
if (error != null) {
throw new ExceptionFactory().newSystemException(error);
}
}
@Override
public int getStatus() throws SystemException {
return status;
}
@Override
public void registerSynchronization(Synchronization synchronization) throws RollbackException, IllegalStateException, SystemException {
debug("TxImpl - registering sync listener: " + synchronization);
listeners.add(synchronization);
}
@Override
public void rollback() throws IllegalStateException, SystemException {
Exception error = null;
debug("TxImpl - rollback");
try {
rollbackConnection();
} catch (Exception ex) {
error = ex;
}
// Call afterCompletion callback and clean up
invokeAfterCompletion();
cleanup();
// Throw any error that may have occurred while rolling back
if (error != null) {
throw new ExceptionFactory().newSystemException(error);
}
}
@Override
public void setRollbackOnly() throws IllegalStateException, SystemException {
debug("TxImpl - setRollbackOnly");
status = STATUS_MARKED_ROLLBACK;
}
/*****************************************/
/***** NOT supported Transaction API *****/
/*****************************************/
@Override
public boolean enlistResource(XAResource xaresource) throws RollbackException, IllegalStateException, SystemException {
return false;
}
@Override
public boolean delistResource(XAResource xaresource, int i) throws IllegalStateException, SystemException {
return false;
}
}