View Javadoc
1   /*
2   
3     Licensed to the Apache Software Foundation (ASF) under one or more
4     contributor license agreements.  See the NOTICE file distributed with
5     this work for additional information regarding copyright ownership.
6     The ASF licenses this file to You under the Apache License, Version 2.0
7     (the "License"); you may not use this file except in compliance with
8     the License.  You may obtain a copy of the License at
9   
10        http://www.apache.org/licenses/LICENSE-2.0
11  
12     Unless required by applicable law or agreed to in writing, software
13     distributed under the License is distributed on an "AS IS" BASIS,
14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15     See the License for the specific language governing permissions and
16     limitations under the License.
17   */
18  package org.apache.commons.dbcp2.managed;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertThrows;
22  
23  import java.lang.reflect.Field;
24  import java.sql.Connection;
25  import java.sql.SQLException;
26  import java.time.Duration;
27  import java.util.Properties;
28  
29  import javax.transaction.HeuristicMixedException;
30  import javax.transaction.HeuristicRollbackException;
31  import javax.transaction.RollbackException;
32  import javax.transaction.Synchronization;
33  import javax.transaction.SystemException;
34  import javax.transaction.Transaction;
35  import javax.transaction.TransactionManager;
36  import javax.transaction.xa.XAResource;
37  
38  import org.apache.commons.dbcp2.ConnectionFactory;
39  import org.apache.commons.dbcp2.Constants;
40  import org.apache.commons.dbcp2.DriverConnectionFactory;
41  import org.apache.commons.dbcp2.PoolableConnection;
42  import org.apache.commons.dbcp2.PoolableConnectionFactory;
43  import org.apache.commons.dbcp2.PoolingDataSource;
44  import org.apache.commons.dbcp2.TesterDriver;
45  import org.apache.commons.pool2.impl.GenericObjectPool;
46  import org.apache.geronimo.transaction.manager.TransactionManagerImpl;
47  import org.junit.jupiter.api.AfterEach;
48  import org.junit.jupiter.api.BeforeEach;
49  import org.junit.jupiter.api.Test;
50  
51  /**
52   * TestSuite for ManagedConnection.
53   */
54  public class TestManagedConnection {
55  
56      private final class UncooperativeLocalXAConnectionFactory
57          extends LocalXAConnectionFactory {
58  
59          public UncooperativeLocalXAConnectionFactory(final TransactionManager transactionManager, final ConnectionFactory connectionFactory) {
60              super(transactionManager, connectionFactory);
61  
62              try {
63                  // inject our own TransactionRegistry which returns Uncooperative Transactions which always fail to enlist a XAResource
64                  final Field field = LocalXAConnectionFactory.class.getDeclaredField("transactionRegistry");
65                  field.setAccessible(true);
66                  field.set(this, new UncooperativeTransactionRegistry(transactionManager));
67              } catch (final Exception e) {
68                  e.printStackTrace();
69              }
70          }
71      }
72  
73      /**
74       * Transaction that always fails enlistResource.
75       */
76      private static final class UncooperativeTransaction
77          implements Transaction {
78  
79          private final Transaction wrappedTransaction;
80  
81          public UncooperativeTransaction(final Transaction transaction) {
82              this.wrappedTransaction = transaction;
83          }
84  
85          @Override
86          public void commit()
87              throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SecurityException,
88              SystemException {
89              wrappedTransaction.commit();
90          }
91  
92          @Override
93          public boolean delistResource(final XAResource arg0, final int arg1)
94              throws IllegalStateException, SystemException {
95              return wrappedTransaction.delistResource(arg0, arg1);
96          }
97  
98          @Override
99          public synchronized boolean enlistResource(final XAResource xaRes) {
100             return false;
101         }
102 
103         @Override
104         public int getStatus()
105             throws SystemException {
106             return wrappedTransaction.getStatus();
107         }
108 
109         @Override
110         public void registerSynchronization(final Synchronization arg0)
111             throws IllegalStateException, RollbackException, SystemException {
112             wrappedTransaction.registerSynchronization(arg0);
113         }
114 
115         @Override
116         public void rollback()
117             throws IllegalStateException, SystemException {
118             wrappedTransaction.rollback();
119         }
120 
121         @Override
122         public void setRollbackOnly()
123             throws IllegalStateException, SystemException {
124             wrappedTransaction.setRollbackOnly();
125         }
126     }
127 
128     private final class UncooperativeTransactionRegistry
129         extends TransactionRegistry {
130 
131         public UncooperativeTransactionRegistry(final TransactionManager transactionManager) {
132             super(transactionManager);
133         }
134 
135         @Override
136         public TransactionContext getActiveTransactionContext()
137             throws SQLException {
138             try {
139                 return new TransactionContext(this, new UncooperativeTransaction(transactionManager.getTransaction()));
140             } catch (final SystemException e) {
141                 return null;
142             }
143         }
144 
145     }
146 
147     protected PoolingDataSource<PoolableConnection> ds;
148 
149     private GenericObjectPool<PoolableConnection> pool;
150 
151     protected TransactionManager transactionManager;
152 
153     public Connection getConnection()
154         throws Exception {
155         return ds.getConnection();
156     }
157 
158     @BeforeEach
159     public void setUp() throws Exception {
160         // create a GeronimoTransactionManager for testing
161         transactionManager = new TransactionManagerImpl();
162 
163         // create a driver connection factory
164         final Properties properties = new Properties();
165         properties.setProperty(Constants.KEY_USER, "userName");
166         properties.setProperty(Constants.KEY_PASSWORD, "password");
167         final ConnectionFactory connectionFactory = new DriverConnectionFactory(new TesterDriver(), "jdbc:apache:commons:testdriver", properties);
168 
169         // wrap it with a LocalXAConnectionFactory
170         final XAConnectionFactory xaConnectionFactory = new UncooperativeLocalXAConnectionFactory(transactionManager, connectionFactory);
171 
172         // create the pool object factory
173         final PoolableConnectionFactory factory = new PoolableConnectionFactory(xaConnectionFactory, null);
174         factory.setValidationQuery("SELECT DUMMY FROM DUAL");
175         factory.setDefaultReadOnly(Boolean.TRUE);
176         factory.setDefaultAutoCommit(Boolean.TRUE);
177 
178         // create the pool
179         pool = new GenericObjectPool<>(factory);
180         factory.setPool(pool);
181         pool.setMaxTotal(10);
182         pool.setMaxWait(Duration.ofMillis(100));
183 
184         // finally create the datasource
185         ds = new ManagedDataSource<>(pool, xaConnectionFactory.getTransactionRegistry());
186         ds.setAccessToUnderlyingConnectionAllowed(true);
187     }
188 
189     @AfterEach
190     public void tearDown()
191         throws Exception {
192         pool.close();
193     }
194 
195     @Test
196     public void testConnectionReturnOnErrorWhenEnlistingXAResource() throws Exception {
197         // see DBCP-433
198         transactionManager.begin();
199         assertThrows(SQLException.class, this::getConnection);
200         transactionManager.commit();
201         assertEquals(1, pool.getBorrowedCount());
202         // assertEquals(1, pool.getReturnedCount());
203         assertEquals(1, pool.getDestroyedCount());
204         assertEquals(0, pool.getNumActive());
205     }
206 
207 }