| /* |
| * 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(), 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; |
| } |
| } |