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        https://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  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import java.sql.Connection;
25  import java.sql.PreparedStatement;
26  import java.sql.ResultSet;
27  import java.sql.SQLException;
28  import java.sql.Timestamp;
29  import java.time.Duration;
30  
31  import javax.transaction.RollbackException;
32  import javax.transaction.Status;
33  
34  import org.apache.commons.dbcp2.Utils;
35  import org.junit.jupiter.api.AfterEach;
36  import org.junit.jupiter.api.Assertions;
37  import org.junit.jupiter.api.BeforeEach;
38  import org.junit.jupiter.api.Test;
39  
40  import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple;
41  import com.arjuna.ats.jta.common.jtaPropertyManager;
42  
43  /**
44   * Requires Java 8 or above.
45   */
46  public class TestConnectionWithNarayana {
47      private static final String CREATE_STMT = "CREATE TABLE TEST_DATA (KEY1 VARCHAR(100), ID BIGINT, VALUE1 DOUBLE PRECISION, INFO TEXT, TS TIMESTAMP)";
48      private static final String INSERT_STMT = "INSERT INTO TEST_DATA   (KEY1, ID, VALUE1, INFO, TS) VALUES (?,?,?,?,?)";
49      private static final String SELECT_STMT = "SELECT KEY1, ID, VALUE1, INFO, TS FROM TEST_DATA LIMIT 1";
50      private static final String PAYLOAD;
51      private static final String DROP_STMT = "DROP TABLE TEST_DATA";
52  
53      static {
54          final StringBuilder sb = new StringBuilder();
55          sb.append("Start");
56          sb.append("payload");
57          for (int i = 0; i < 10000; i++) {
58              sb.append("...");
59              sb.append(i);
60          }
61          sb.append("End");
62          sb.append("payload");
63  
64          PAYLOAD = sb.toString();
65      }
66  
67      private BasicManagedDataSource mds;
68  
69      @BeforeEach
70      public void setUp() throws Exception {
71          jtaPropertyManager.getJTAEnvironmentBean()
72                  .setLastResourceOptimisationInterfaceClassName("org.apache.commons.dbcp2.managed.LocalXAConnectionFactory$LocalXAResource");
73          mds = new BasicManagedDataSource();
74          mds.setTransactionManager(new TransactionManagerImple());
75          mds.setDriverClassName("org.h2.Driver");
76          mds.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
77  
78          mds.setMaxTotal(80);
79          mds.setMinIdle(0);
80          mds.setMaxIdle(80);
81          mds.setMinEvictableIdle(Duration.ofSeconds(10));
82          mds.setDurationBetweenEvictionRuns(Duration.ofSeconds(10));
83          mds.setLogAbandoned(true);
84          mds.setMaxWait(Duration.ofSeconds(2));
85          mds.setRemoveAbandonedOnMaintenance(true);
86          mds.setRemoveAbandonedOnBorrow(true);
87  
88          mds.setRemoveAbandonedTimeout(Duration.ofSeconds(10));
89          mds.setLogExpiredConnections(true);
90          mds.setLifo(false);
91  
92          try (final Connection conn = mds.getConnection()) {
93              try (final PreparedStatement ps = conn.prepareStatement(CREATE_STMT)) {
94                  ps.execute();
95              }
96          }
97      }
98  
99      @AfterEach
100     public void tearDown() throws Exception {
101         try (final Connection conn = mds.getConnection()) {
102             try (final PreparedStatement ps = conn.prepareStatement(DROP_STMT)) {
103                 ps.execute();
104             }
105         }
106         Utils.closeQuietly(mds);
107     }
108 
109     @Test
110     void testConnectionCommitAfterTimeout() throws Exception {
111         mds.getTransactionManager().setTransactionTimeout(1);
112         mds.getTransactionManager().begin();
113         try (Connection conn = mds.getConnection()) {
114             do {
115                 Thread.sleep(1000);
116             } while (mds.getTransactionManager().getTransaction().getStatus() != Status.STATUS_ROLLEDBACK);
117             // Let the reaper do it's thing
118             Thread.sleep(1000);
119             final SQLException e = assertThrows(SQLException.class, conn::commit);
120             assertEquals("Commit cannot be set while enrolled in a transaction", e.getMessage(), "Should not work after timeout");
121             mds.getTransactionManager().rollback();
122         }
123         assertEquals(0, mds.getNumActive());
124     }
125 
126     @Test
127     void testConnectionInTimeout() throws Exception {
128         Connection conn = null;
129         PreparedStatement ps = null;
130         for (int i = 0; i < 5; i++) {
131             try {
132                 mds.getTransactionManager().setTransactionTimeout(1);
133                 mds.getTransactionManager().begin();
134 
135                 conn = mds.getConnection();
136                 ps = conn.prepareStatement(INSERT_STMT);
137                 ps.setString(1, Thread.currentThread().getName());
138                 ps.setLong(2, i);
139                 ps.setDouble(3, new java.util.Random().nextDouble());
140                 ps.setString(4, PAYLOAD);
141                 ps.setTimestamp(5, new Timestamp(System.currentTimeMillis()));
142                 ps.execute();
143 
144                 int n = 0;
145                 do {
146                     if (mds.getTransactionManager().getTransaction().getStatus() != Status.STATUS_ACTIVE) {
147                         n++;
148                     }
149                     try (Connection c = mds.getConnection(); PreparedStatement ps2 = c.prepareStatement(SELECT_STMT); ResultSet rs = ps2.executeQuery()) {
150                         // nothing here, all auto-close.
151                     }
152                 } while (n < 2);
153 
154                 ps.close();
155                 ps = null;
156                 conn.close();
157                 conn = null;
158 
159                 assertThrows(RollbackException.class, () -> mds.getTransactionManager().commit());
160                 // this is expected
161                 if (mds.getTransactionManager().getTransaction() != null) {
162                     // Need to pop it off the thread if a background thread rolled the transaction
163                     // back
164                     mds.getTransactionManager().rollback();
165                 }
166             } catch (final Exception e) {
167                 if (mds.getTransactionManager().getTransaction() != null) {
168                     // Need to pop it off the thread if a background thread rolled the transaction
169                     // back
170                     mds.getTransactionManager().rollback();
171                 }
172             } finally {
173                 if (ps != null) {
174                     ps.close();
175                 }
176                 if (conn != null) {
177                     conn.close();
178                 }
179             }
180             Assertions.assertEquals(0, mds.getNumActive());
181         }
182     }
183 
184     @Test
185     void testRepeatedGetConnectionInTimeout() throws Exception {
186         mds.getTransactionManager().setTransactionTimeout(1);
187         mds.getTransactionManager().begin();
188         try {
189             do {
190                 Thread.sleep(1000);
191             } while (mds.getTransactionManager().getTransaction().getStatus() != Status.STATUS_ROLLEDBACK);
192             // Let the reaper do it's thing
193             Thread.sleep(1000);
194             final SQLException e = assertThrows(SQLException.class, mds::getConnection);
195             assertTrue(e.getCause().getClass().equals(IllegalStateException.class));
196             final SQLException e2 = assertThrows(SQLException.class, mds::getConnection);
197             assertTrue(e2.getCause().getClass().equals(IllegalStateException.class));
198         } finally {
199             mds.getTransactionManager().rollback();
200         }
201         Assertions.assertEquals(0, mds.getNumActive());
202     }
203 }