TransactionRegistry.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.dbcp2.managed;

  18. import java.sql.Connection;
  19. import java.sql.SQLException;
  20. import java.util.Map;
  21. import java.util.Objects;
  22. import java.util.WeakHashMap;

  23. import javax.transaction.SystemException;
  24. import javax.transaction.Transaction;
  25. import javax.transaction.TransactionManager;
  26. import javax.transaction.TransactionSynchronizationRegistry;
  27. import javax.transaction.xa.XAResource;

  28. import org.apache.commons.dbcp2.DelegatingConnection;

  29. /**
  30.  * TransactionRegistry tracks Connections and XAResources in a transacted environment for a single XAConnectionFactory.
  31.  * <p>
  32.  * The TransactionRegistry hides the details of transaction processing from the existing DBCP pooling code, and gives
  33.  * the ManagedConnection a way to enlist connections in a transaction, allowing for the maximal rescue of DBCP.
  34.  * </p>
  35.  *
  36.  * @since 2.0
  37.  */
  38. public class TransactionRegistry {
  39.     private final TransactionManager transactionManager;
  40.     private final Map<Transaction, TransactionContext> caches = new WeakHashMap<>();
  41.     private final Map<Connection, XAResource> xaResources = new WeakHashMap<>();
  42.     private final TransactionSynchronizationRegistry transactionSynchronizationRegistry;

  43.     /**
  44.      * Provided for backwards compatibility
  45.      * @param transactionManager the transaction manager used to enlist connections
  46.      */
  47.     public TransactionRegistry(final TransactionManager transactionManager) {
  48.         this (transactionManager, null);
  49.     }

  50.     /**
  51.      * Creates a TransactionRegistry for the specified transaction manager.
  52.      *
  53.      * @param transactionManager
  54.      *            the transaction manager used to enlist connections.
  55.      * @param transactionSynchronizationRegistry
  56.      *              The optional TSR to register synchronizations with
  57.      * @since 2.6.0
  58.      */
  59.     public TransactionRegistry(final TransactionManager transactionManager, final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
  60.         this.transactionManager = transactionManager;
  61.         this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
  62.     }

  63.     /**
  64.      * Gets the active TransactionContext or null if not Transaction is active.
  65.      *
  66.      * @return The active TransactionContext or null if no Transaction is active.
  67.      * @throws SQLException
  68.      *             Thrown when an error occurs while fetching the transaction.
  69.      */
  70.     public TransactionContext getActiveTransactionContext() throws SQLException {
  71.         Transaction transaction = null;
  72.         try {
  73.             transaction = transactionManager.getTransaction();

  74.             // was there a transaction?
  75.             if (transaction == null) {
  76.                 return null;
  77.             }

  78.             // This is the transaction on the thread so no need to check its status - we should try to use it and
  79.             // fail later based on the subsequent status
  80.         } catch (final SystemException e) {
  81.             throw new SQLException("Unable to determine current transaction ", e);
  82.         }

  83.         // register the context (or create a new one)
  84.         synchronized (this) {
  85.             return caches.computeIfAbsent(transaction, k -> new TransactionContext(this, k, transactionSynchronizationRegistry));
  86.         }
  87.     }

  88.     private Connection getConnectionKey(final Connection connection) {
  89.         final Connection result;
  90.         if (connection instanceof DelegatingConnection) {
  91.             result = ((DelegatingConnection<?>) connection).getInnermostDelegateInternal();
  92.         } else {
  93.             result = connection;
  94.         }
  95.         return result;
  96.     }

  97.     /**
  98.      * Gets the XAResource registered for the connection.
  99.      *
  100.      * @param connection
  101.      *            the connection
  102.      * @return The XAResource registered for the connection; never null.
  103.      * @throws SQLException
  104.      *             Thrown when the connection does not have a registered XAResource.
  105.      */
  106.     public synchronized XAResource getXAResource(final Connection connection) throws SQLException {
  107.         Objects.requireNonNull(connection, "connection");
  108.         final Connection key = getConnectionKey(connection);
  109.         final XAResource xaResource = xaResources.get(key);
  110.         if (xaResource == null) {
  111.             throw new SQLException("Connection does not have a registered XAResource " + connection);
  112.         }
  113.         return xaResource;
  114.     }

  115.     /**
  116.      * Registers the association between a Connection and a XAResource. When a connection is enlisted in a transaction,
  117.      * it is actually the XAResource that is given to the transaction manager.
  118.      *
  119.      * @param connection
  120.      *            The JDBC connection.
  121.      * @param xaResource
  122.      *            The XAResource which managed the connection within a transaction.
  123.      */
  124.     public synchronized void registerConnection(final Connection connection, final XAResource xaResource) {
  125.         Objects.requireNonNull(connection, "connection");
  126.         Objects.requireNonNull(xaResource, "xaResource");
  127.         xaResources.put(connection, xaResource);
  128.     }

  129.     /**
  130.      * Unregisters a destroyed connection from {@link TransactionRegistry}.
  131.      *
  132.      * @param connection
  133.      *            A destroyed connection from {@link TransactionRegistry}.
  134.      */
  135.     public synchronized void unregisterConnection(final Connection connection) {
  136.         xaResources.remove(getConnectionKey(connection));
  137.     }
  138. }