1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
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
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
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
161 transactionManager = new TransactionManagerImpl();
162
163
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
170 final XAConnectionFactory xaConnectionFactory = new UncooperativeLocalXAConnectionFactory(transactionManager, connectionFactory);
171
172
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
179 pool = new GenericObjectPool<>(factory);
180 factory.setPool(pool);
181 pool.setMaxTotal(10);
182 pool.setMaxWait(Duration.ofMillis(100));
183
184
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
198 transactionManager.begin();
199 assertThrows(SQLException.class, this::getConnection);
200 transactionManager.commit();
201 assertEquals(1, pool.getBorrowedCount());
202
203 assertEquals(1, pool.getDestroyedCount());
204 assertEquals(0, pool.getNumActive());
205 }
206
207 }