1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.dbcp2;
19
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
23 import static org.junit.jupiter.api.Assertions.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertNull;
25 import static org.junit.jupiter.api.Assertions.assertSame;
26 import static org.junit.jupiter.api.Assertions.assertThrows;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28
29 import java.io.IOException;
30 import java.lang.management.ManagementFactory;
31 import java.lang.management.ThreadMXBean;
32 import java.sql.Connection;
33 import java.sql.SQLException;
34 import java.time.Duration;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Collection;
38 import java.util.HashSet;
39 import java.util.Properties;
40 import java.util.Set;
41 import java.util.concurrent.atomic.AtomicInteger;
42 import java.util.stream.Stream;
43
44 import javax.management.AttributeNotFoundException;
45 import javax.management.MBeanAttributeInfo;
46 import javax.management.MBeanServer;
47 import javax.management.ObjectName;
48 import javax.sql.DataSource;
49
50 import org.junit.jupiter.api.AfterEach;
51 import org.junit.jupiter.api.Assertions;
52 import org.junit.jupiter.api.BeforeEach;
53 import org.junit.jupiter.api.Disabled;
54 import org.junit.jupiter.api.Test;
55
56
57
58
59 public class TestBasicDataSource extends TestConnectionPool {
60
61 private static final String CATALOG = "test catalog";
62
63 protected BasicDataSource ds;
64
65
66
67
68
69
70
71 private void checkIdleValid() throws Exception {
72 final Set<Connection> idleConnections = new HashSet<>();
73
74 for (int i = 0; i < ds.getNumIdle(); i++) {
75 final Connection conn = ds.getConnection();
76 idleConnections.add(conn);
77 }
78
79 for (final Connection conn : idleConnections) {
80 assertTrue(conn.isValid(2), "Connection should be valid");
81 conn.close();
82 }
83 }
84
85
86
87
88
89
90 private void checkLimits() throws Exception {
91 assertTrue(ds.getNumActive() + ds.getNumIdle() <= getMaxTotal(), "Total connections exceed maxTotal");
92 assertTrue(ds.getNumIdle() <= ds.getMaxIdle(), "Idle connections exceed maxIdle");
93 }
94
95 protected BasicDataSource createDataSource() throws Exception {
96 return new BasicDataSource();
97 }
98
99 @Override
100 protected Connection getConnection() throws Exception {
101 return ds.getConnection();
102 }
103
104 @BeforeEach
105 public void setUp() throws Exception {
106 ds = createDataSource();
107 ds.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
108 ds.setUrl("jdbc:apache:commons:testdriver");
109 ds.setMaxTotal(getMaxTotal());
110 ds.setMaxWait(getMaxWaitDuration());
111 ds.setDefaultAutoCommit(Boolean.TRUE);
112 ds.setDefaultReadOnly(Boolean.FALSE);
113 ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
114 ds.setDefaultCatalog(CATALOG);
115 ds.setUsername("userName");
116 ds.setPassword("password");
117 ds.setValidationQuery("SELECT DUMMY FROM DUAL");
118 ds.setConnectionInitSqls(Arrays.asList("SELECT 1", "SELECT 2"));
119 ds.setDriverClassLoader(new TesterClassLoader());
120 ds.setJmxName("org.apache.commons.dbcp2:name=test");
121 }
122
123 @Override
124 @AfterEach
125 public void tearDown() throws Exception {
126 super.tearDown();
127 ds.close();
128 ds = null;
129 }
130
131 @Test
132 void testAccessToUnderlyingConnectionAllowed() throws Exception {
133 ds.setAccessToUnderlyingConnectionAllowed(true);
134 assertTrue(ds.isAccessToUnderlyingConnectionAllowed());
135
136 try (final Connection conn = getConnection()) {
137 Connection dconn = ((DelegatingConnection<?>) conn).getDelegate();
138 assertNotNull(dconn);
139
140 dconn = ((DelegatingConnection<?>) conn).getInnermostDelegate();
141 assertNotNull(dconn);
142
143 assertInstanceOf(TesterConnection.class, dconn);
144 }
145 }
146
147 @Test
148 void testClose() throws Exception {
149 ds.setAccessToUnderlyingConnectionAllowed(true);
150
151
152 final Connection activeConnection = getConnection();
153 final Connection rawActiveConnection = ((DelegatingConnection<?>) activeConnection).getInnermostDelegate();
154 assertFalse(activeConnection.isClosed());
155 assertFalse(rawActiveConnection.isClosed());
156
157
158 final Connection idleConnection = getConnection();
159 final Connection rawIdleConnection = ((DelegatingConnection<?>) idleConnection).getInnermostDelegate();
160 assertFalse(idleConnection.isClosed());
161 assertFalse(rawIdleConnection.isClosed());
162
163
164 idleConnection.close();
165 assertTrue(idleConnection.isClosed());
166 assertFalse(rawIdleConnection.isClosed());
167
168 ds.close();
169
170
171 assertTrue(rawIdleConnection.isClosed());
172
173
174 assertFalse(activeConnection.isClosed());
175 assertFalse(rawActiveConnection.isClosed());
176
177
178 activeConnection.close();
179
180
181 assertTrue(activeConnection.isClosed());
182 assertTrue(rawActiveConnection.isClosed());
183
184
185 assertThrows(SQLException.class, this::getConnection);
186
187
188 ds.close();
189
190 }
191
192 @Test
193 void testConcurrentInitBorrow() throws Exception {
194 ds.setDriverClassName("org.apache.commons.dbcp2.TesterConnectionDelayDriver");
195 ds.setUrl("jdbc:apache:commons:testerConnectionDelayDriver:50");
196 ds.setInitialSize(8);
197
198
199 final TestThread testThread = new TestThread(1, 0);
200 final Thread t = new Thread(testThread);
201 t.start();
202
203
204 Thread.sleep(100);
205 try (Connection conn = ds.getConnection()) {
206
207
208
209 assertTrue(ds.getConnectionPool().getNumIdle() > 5);
210
211
212 t.join();
213 assertFalse(testThread.failed());
214 }
215 ds.close();
216 }
217
218
219
220
221 @Test
222 void testConcurrentInvalidateBorrow() throws Exception {
223 ds.setDriverClassName("org.apache.commons.dbcp2.TesterConnRequestCountDriver");
224 ds.setUrl("jdbc:apache:commons:testerConnRequestCountDriver");
225 ds.setTestOnBorrow(true);
226 ds.setValidationQuery("SELECT DUMMY FROM DUAL");
227 ds.setMaxTotal(8);
228 ds.setLifo(true);
229 ds.setMaxWait(Duration.ofMillis(-1));
230
231
232 final TestThread testThread1 = new TestThread(1000, 0);
233 final Thread t1 = new Thread(testThread1);
234 t1.start();
235 final TestThread testThread2 = new TestThread(1000, 0);
236 final Thread t2 = new Thread(testThread1);
237 t2.start();
238
239
240 for (int i = 0; i < 1000; i++) {
241 final Connection conn = ds.getConnection();
242 ds.invalidateConnection(conn);
243 }
244
245
246 t1.join();
247 t2.join();
248 assertFalse(testThread1.failed());
249 assertFalse(testThread2.failed());
250
251 ds.close();
252 }
253
254
255
256
257 @Test
258 void testConnectionMBeansDisabled() throws Exception {
259 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
260
261 final ObjectName commons = new ObjectName("org.apache.commons.*:*");
262 final Set<ObjectName> results = mbs.queryNames(commons, null);
263 for (final ObjectName result : results) {
264 mbs.unregisterMBean(result);
265 }
266 ds.setRegisterConnectionMBean(false);
267 try (Connection conn = ds.getConnection()) {
268
269 final ObjectName connections = new ObjectName("org.apache.commons.*:connection=*,*");
270 assertEquals(0, mbs.queryNames(connections, null).size());
271 }
272 }
273
274
275
276
277 @Test
278 void testCreateConnectionFactoryWithConnectionFactoryClassName() throws Exception {
279 Properties properties = new Properties();
280
281 properties = new Properties();
282 properties.put("initialSize", "1");
283 properties.put("driverClassName", "org.apache.commons.dbcp2.TesterDriver");
284 properties.put("url", "jdbc:apache:commons:testdriver");
285 properties.put("username", "foo");
286 properties.put("password", "bar");
287 properties.put("connectionFactoryClassName", "org.apache.commons.dbcp2.TesterConnectionFactory");
288 try (BasicDataSource ds = BasicDataSourceFactory.createDataSource(properties)) {
289 try (Connection conn = ds.getConnection()) {
290 assertNotNull(conn);
291 }
292 }
293 }
294
295
296
297
298 @Test
299 void testCreateConnectionFactoryWithoutConnectionFactoryClassName() throws Exception {
300
301 final Properties properties = new Properties();
302 properties.put("initialSize", "1");
303 properties.put("driverClassName", "org.apache.commons.dbcp2.TesterDriver");
304 properties.put("url", "jdbc:apache:commons:testdriver");
305 properties.put("username", "foo");
306 properties.put("password", "bar");
307 try (BasicDataSource ds = BasicDataSourceFactory.createDataSource(properties)) {
308 try (Connection conn = ds.getConnection()) {
309 assertNotNull(conn);
310 }
311 }
312 }
313
314
315
316
317 @Test
318 void testCreateDataSourceCleanupEvictor() throws Exception {
319 ds.close();
320 ds = null;
321 ds = createDataSource();
322 ds.setDriverClassName("org.apache.commons.dbcp2.TesterConnRequestCountDriver");
323 ds.setUrl("jdbc:apache:commons:testerConnRequestCountDriver");
324 ds.setValidationQuery("SELECT DUMMY FROM DUAL");
325 ds.setUsername("userName");
326
327
328 ds.setPassword("wrong");
329
330 ds.setDurationBetweenEvictionRuns(Duration.ofMillis(100));
331
332 ds.setMinIdle(2);
333
334
335 synchronized (TesterConnRequestCountDriver.class) {
336 TesterConnRequestCountDriver.initConnRequestCount();
337
338
339 for (int i = 0; i < 10; i++) {
340 try {
341 @SuppressWarnings("unused")
342 final DataSource ds2 = ds.createDataSource();
343 } catch (final SQLException e) {
344
345 }
346 }
347
348
349 Thread.sleep(1000);
350
351
352 assertEquals(10, TesterConnRequestCountDriver.getConnectionRequestCount());
353 }
354
355
356 assertNull(ds.getConnectionPool());
357 }
358
359
360
361
362 @Test
363 void testCreateDataSourceCleanupThreads() throws Exception {
364 ds.close();
365 ds = null;
366 ds = createDataSource();
367 ds.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
368 ds.setUrl("jdbc:apache:commons:testdriver");
369 ds.setMaxTotal(getMaxTotal());
370 ds.setMaxWait(getMaxWaitDuration());
371 ds.setDefaultAutoCommit(Boolean.TRUE);
372 ds.setDefaultReadOnly(Boolean.FALSE);
373 ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
374 ds.setDefaultCatalog(CATALOG);
375 ds.setUsername("userName");
376
377 ds.setDurationBetweenEvictionRuns(Duration.ofMillis(100));
378
379 ds.setPassword("wrong");
380 ds.setValidationQuery("SELECT DUMMY FROM DUAL");
381 final int threadCount = Thread.activeCount();
382 for (int i = 0; i < 10; i++) {
383 try (Connection c = ds.getConnection()) {
384 } catch (final SQLException ex) {
385
386 }
387 }
388
389 assertTrue(Thread.activeCount() <= threadCount + 1);
390 }
391
392 @Test
393 void testDefaultCatalog() throws Exception {
394 final Connection[] c = new Connection[getMaxTotal()];
395 for (int i = 0; i < c.length; i++) {
396 c[i] = getConnection();
397 assertNotNull(c[i]);
398 assertEquals(CATALOG, c[i].getCatalog());
399 }
400
401 for (final Connection element : c) {
402 element.setCatalog("error");
403 element.close();
404 }
405
406 for (int i = 0; i < c.length; i++) {
407 c[i] = getConnection();
408 assertNotNull(c[i]);
409 assertEquals(CATALOG, c[i].getCatalog());
410 }
411
412 for (final Connection element : c) {
413 element.close();
414 }
415 }
416
417 @Test
418 void testDeprecatedAccessors() throws SQLException {
419 try (BasicDataSource bds = new BasicDataSource()) {
420 int i = 0;
421
422 i++;
423 bds.setDefaultQueryTimeout(i);
424 assertEquals(i, bds.getDefaultQueryTimeout());
425 assertEquals(Duration.ofSeconds(i), bds.getDefaultQueryTimeoutDuration());
426
427 i++;
428 bds.setMaxConnLifetimeMillis(i);
429 assertEquals(i, bds.getMaxConnLifetimeMillis());
430 assertEquals(Duration.ofMillis(i), bds.getMaxConnDuration());
431
432 i++;
433 bds.setMaxWaitMillis(i);
434 assertEquals(i, bds.getMaxWaitMillis());
435 assertEquals(Duration.ofMillis(i), bds.getMaxWaitDuration());
436
437 i++;
438 bds.setMinEvictableIdleTimeMillis(i);
439 assertEquals(i, bds.getMinEvictableIdleTimeMillis());
440 assertEquals(Duration.ofMillis(i), bds.getMinEvictableIdleDuration());
441
442 i++;
443 bds.setRemoveAbandonedTimeout(i);
444 assertEquals(i, bds.getRemoveAbandonedTimeout());
445 assertEquals(Duration.ofSeconds(i), bds.getRemoveAbandonedTimeoutDuration());
446
447 i++;
448 bds.setSoftMinEvictableIdleTimeMillis(i);
449 assertEquals(i, bds.getSoftMinEvictableIdleTimeMillis());
450 assertEquals(Duration.ofMillis(i), bds.getSoftMinEvictableIdleDuration());
451
452 i++;
453 bds.setTimeBetweenEvictionRunsMillis(i);
454 assertEquals(i, bds.getTimeBetweenEvictionRunsMillis());
455 assertEquals(Duration.ofMillis(i), bds.getDurationBetweenEvictionRuns());
456
457 i++;
458 bds.setValidationQueryTimeout(1);
459 assertEquals(1, bds.getValidationQueryTimeout());
460 assertEquals(Duration.ofSeconds(1), bds.getValidationQueryTimeoutDuration());
461 }
462 }
463
464 @Test
465 void testDisconnectionIgnoreSqlCodes() throws Exception {
466 final ArrayList<String> disconnectionIgnoreSqlCodes = new ArrayList<>();
467 disconnectionIgnoreSqlCodes.add("XXXX");
468 ds.setDisconnectionIgnoreSqlCodes(disconnectionIgnoreSqlCodes);
469 ds.setFastFailValidation(true);
470 try (Connection conn = ds.getConnection()) {
471
472 final PoolableConnectionFactory pcf = (PoolableConnectionFactory) ds.getConnectionPool().getFactory();
473 assertTrue(pcf.isFastFailValidation());
474 assertTrue(pcf.getDisconnectionIgnoreSqlCodes().contains("XXXX"));
475 assertEquals(1, pcf.getDisconnectionIgnoreSqlCodes().size());
476 }
477 }
478
479
480
481
482 @Test
483 void testDisconnectSqlCodes() throws Exception {
484 final ArrayList<String> disconnectionSqlCodes = new ArrayList<>();
485 disconnectionSqlCodes.add("XXX");
486 ds.setDisconnectionSqlCodes(disconnectionSqlCodes);
487 ds.setFastFailValidation(true);
488 try (Connection conn = ds.getConnection()) {
489
490 final PoolableConnectionFactory pcf = (PoolableConnectionFactory) ds.getConnectionPool().getFactory();
491 assertTrue(pcf.isFastFailValidation());
492 assertTrue(pcf.getDisconnectionSqlCodes().contains("XXX"));
493 assertEquals(1, pcf.getDisconnectionSqlCodes().size());
494 }
495 }
496
497
498
499
500
501
502 @Test
503 void testDriverClassLoader() throws Exception {
504 try (Connection conn = getConnection()) {
505 final ClassLoader cl = ds.getDriverClassLoader();
506 assertNotNull(cl);
507 assertInstanceOf(TesterClassLoader.class, cl);
508 assertTrue(((TesterClassLoader) cl).didLoad(ds.getDriverClassName()));
509 }
510 }
511
512 @Test
513 void testEmptyInitConnectionSql() throws Exception {
514
515 ds.setConnectionInitSqls(Arrays.asList("", " "));
516 assertNotNull(ds.getConnectionInitSqls());
517 assertEquals(0, ds.getConnectionInitSqls().size());
518
519 ds.setConnectionInitSqls(null);
520 assertNotNull(ds.getConnectionInitSqls());
521 assertEquals(0, ds.getConnectionInitSqls().size());
522
523 ds.setConnectionInitSqls((Collection<String>) Arrays.asList("", " "));
524 assertNotNull(ds.getConnectionInitSqls());
525 assertEquals(0, ds.getConnectionInitSqls().size());
526 }
527
528 @Test
529 void testEmptyValidationQuery() throws Exception {
530 assertNotNull(ds.getValidationQuery());
531
532 ds.setValidationQuery("");
533 assertNull(ds.getValidationQuery());
534
535 ds.setValidationQuery(" ");
536 assertNull(ds.getValidationQuery());
537 }
538
539 @Test
540 @Disabled
541 void testEvict() throws Exception {
542 final long delay = 1000;
543
544 ds.setInitialSize(10);
545 ds.setMaxIdle(10);
546 ds.setMaxTotal(10);
547 ds.setMinIdle(5);
548 ds.setNumTestsPerEvictionRun(3);
549 ds.setMinEvictableIdle(Duration.ofMillis(100));
550 ds.setDurationBetweenEvictionRuns(Duration.ofMillis(delay));
551 ds.setPoolPreparedStatements(true);
552
553 try (Connection conn = ds.getConnection()) {
554
555 }
556
557 final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
558 while (Stream.of(threadBean.getThreadInfo(threadBean.getAllThreadIds())).anyMatch(t -> t.getThreadName().equals("commons-pool-evictor-thread"))) {
559 if (ds.getNumIdle() <= ds.getMinIdle()) {
560 break;
561 }
562 Thread.sleep(delay);
563 }
564 assertFalse(ds.getNumIdle() > ds.getMinIdle(),
565 () -> "EvictionTimer thread was destroyed with numIdle=" + ds.getNumIdle() + "(expected: less or equal than " + ds.getMinIdle() + ")");
566 }
567
568 @Test
569 void testInitialSize() throws Exception {
570 ds.setMaxTotal(20);
571 ds.setMaxIdle(20);
572 ds.setInitialSize(10);
573
574 try (Connection conn = getConnection()) {
575 assertNotNull(conn);
576 }
577
578 assertEquals(0, ds.getNumActive());
579 assertEquals(10, ds.getNumIdle());
580 }
581
582
583
584
585 @Test
586 void testInstanceNotFoundExceptionLogSuppressed() throws Exception {
587 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
588 try (Connection c = ds.getConnection()) {
589
590 }
591 final ObjectName objectName = new ObjectName(ds.getJmxName());
592 if (mbs.isRegistered(objectName)) {
593 mbs.unregisterMBean(objectName);
594 }
595 StackMessageLog.clear();
596 ds.close();
597 assertNull(StackMessageLog.popMessage());
598 assertNull(ds.getRegisteredJmxName());
599 }
600
601
602 @Test
603 void testInvalidateConnection() throws Exception {
604 ds.setMaxTotal(2);
605 try (final Connection conn1 = ds.getConnection()) {
606 try (final Connection conn2 = ds.getConnection()) {
607 ds.invalidateConnection(conn1);
608 assertTrue(conn1.isClosed());
609 assertEquals(1, ds.getNumActive());
610 checkIdleValid();
611 checkLimits();
612 try (final Connection conn3 = ds.getConnection()) {
613 conn2.close();
614 conn3.close();
615 }
616 }
617 }
618 }
619
620 @Test
621 void testInvalidConnectionInitSqlCollection() {
622 ds.setConnectionInitSqls((Collection<String>) Arrays.asList("SELECT 1", "invalid"));
623 final SQLException e = assertThrows(SQLException.class, ds::getConnection);
624 assertTrue(e.toString().contains("invalid"));
625 }
626
627 @Test
628 void testInvalidConnectionInitSqlList() {
629 ds.setConnectionInitSqls(Arrays.asList("SELECT 1", "invalid"));
630 final SQLException e = assertThrows(SQLException.class, ds::getConnection);
631 assertTrue(e.toString().contains("invalid"));
632 }
633
634 @Test
635 void testInvalidValidationQuery() {
636 ds.setValidationQuery("invalid");
637 final SQLException e = assertThrows(SQLException.class, ds::getConnection);
638 assertTrue(e.toString().contains("invalid"));
639 }
640
641
642
643 @Test
644 void testIsClosedFailure() throws SQLException {
645 ds.setAccessToUnderlyingConnectionAllowed(true);
646 final Connection conn = ds.getConnection();
647 assertNotNull(conn);
648 assertEquals(1, ds.getNumActive());
649
650
651 final TesterConnection tconn = (TesterConnection) ((DelegatingConnection<?>) conn).getInnermostDelegate();
652 tconn.setFailure(new IOException("network error"));
653
654 assertThrows(SQLException.class, () -> conn.close());
655
656 assertEquals(0, ds.getNumActive());
657 }
658
659 @Test
660 void testIsWrapperFor() throws Exception {
661 assertTrue(ds.isWrapperFor(BasicDataSource.class));
662 assertTrue(ds.isWrapperFor(AutoCloseable.class));
663 assertFalse(ds.isWrapperFor(String.class));
664 assertFalse(ds.isWrapperFor(null));
665 }
666
667
668
669
670 @Test
671 void testJmxDisabled() throws Exception {
672 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
673
674 final ObjectName commons = new ObjectName("org.apache.commons.*:*");
675 final Set<ObjectName> results = mbs.queryNames(commons, null);
676 for (final ObjectName result : results) {
677 mbs.unregisterMBean(result);
678 }
679 ds.setJmxName(null);
680 ds.setPoolPreparedStatements(true);
681 try (Connection conn = ds.getConnection()) {
682
683 assertEquals(0, mbs.queryNames(commons, null).size());
684 }
685 }
686
687
688
689
690
691
692
693 @Test
694 void testJmxDoesNotExposePassword() throws Exception {
695 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
696 try (Connection c = ds.getConnection()) {
697
698 }
699 final ObjectName objectName = new ObjectName(ds.getJmxName());
700 final MBeanAttributeInfo[] attributes = mbs.getMBeanInfo(objectName).getAttributes();
701 assertTrue(attributes != null && attributes.length > 0);
702 Arrays.asList(attributes).forEach(attrInfo -> assertFalse("password".equalsIgnoreCase(attrInfo.getName())));
703 assertThrows(AttributeNotFoundException.class, () -> mbs.getAttribute(objectName, "Password"));
704 }
705
706 @Test
707 void testManualConnectionEvict() throws Exception {
708 ds.setMinIdle(0);
709 ds.setMaxIdle(4);
710 ds.setMinEvictableIdle(Duration.ofMillis(10));
711 ds.setNumTestsPerEvictionRun(2);
712
713 try (Connection ds2 = ds.createDataSource().getConnection(); Connection ds3 = ds.createDataSource().getConnection()) {
714 assertEquals(0, ds.getNumIdle());
715 }
716
717
718 Thread.sleep(100);
719
720
721 assertEquals(2, ds.getNumIdle());
722
723
724 ds.evict();
725
726
727 assertEquals(0, ds.getNumIdle());
728 }
729
730 @Test
731 void testMaxConnLifetimeExceeded() throws Exception {
732 try {
733 StackMessageLog.lock();
734 ds.setMaxConn(Duration.ofMillis(100));
735 try (Connection conn = ds.getConnection()) {
736 assertEquals(1, ds.getNumActive());
737 Thread.sleep(500);
738 }
739 assertEquals(0, ds.getNumIdle());
740 final String message = StackMessageLog.popMessage();
741 Assertions.assertNotNull(message);
742 assertTrue(message.indexOf("exceeds the maximum permitted value") > 0);
743 } finally {
744 StackMessageLog.clear();
745 StackMessageLog.unLock();
746 }
747 }
748
749 @Test
750 void testMaxConnLifetimeExceededMutedLog() throws Exception {
751 try {
752 StackMessageLog.lock();
753 StackMessageLog.clear();
754 ds.setMaxConn(Duration.ofMillis(100));
755 ds.setLogExpiredConnections(false);
756 try (final Connection conn = ds.getConnection()) {
757 assertEquals(1, ds.getNumActive());
758 Thread.sleep(500);
759 }
760 assertEquals(0, ds.getNumIdle());
761 assertTrue(StackMessageLog.isEmpty(), StackMessageLog.getAll().toString());
762 } finally {
763 StackMessageLog.clear();
764 StackMessageLog.unLock();
765 }
766 }
767
768
769
770
771
772 @Test
773 void testMaxTotalZero() throws Exception {
774 ds.setMaxTotal(0);
775 assertThrows(SQLException.class, ds::getConnection);
776 }
777
778
779
780
781 @Test
782 void testMutateAbandonedConfig() throws Exception {
783 final Properties properties = new Properties();
784 properties.put("initialSize", "1");
785 properties.put("driverClassName", "org.apache.commons.dbcp2.TesterDriver");
786 properties.put("url", "jdbc:apache:commons:testdriver");
787 properties.put("username", "foo");
788 properties.put("password", "bar");
789 try (BasicDataSource ds = BasicDataSourceFactory.createDataSource(properties)) {
790 final boolean original = ds.getConnectionPool().getLogAbandoned();
791 ds.setLogAbandoned(!original);
792 Assertions.assertNotEquals(original, ds.getConnectionPool().getLogAbandoned());
793 }
794 }
795
796 @Test
797 void testNoAccessToUnderlyingConnectionAllowed() throws Exception {
798
799 assertFalse(ds.isAccessToUnderlyingConnectionAllowed());
800
801 try (Connection conn = getConnection()) {
802 Connection dconn = ((DelegatingConnection<?>) conn).getDelegate();
803 assertNull(dconn);
804
805 dconn = ((DelegatingConnection<?>) conn).getInnermostDelegate();
806 assertNull(dconn);
807 }
808 }
809
810 @Test
811 void testNoOverlapBetweenDisconnectionAndIgnoreSqlCodes() {
812
813 final HashSet<String> disconnectionSqlCodes = new HashSet<>(Arrays.asList("XXX", "ZZZ"));
814 ds.setDisconnectionSqlCodes(disconnectionSqlCodes);
815
816
817 final HashSet<String> disconnectionIgnoreSqlCodes = new HashSet<>(Arrays.asList("YYY", "AAA"));
818 ds.setDisconnectionIgnoreSqlCodes(disconnectionIgnoreSqlCodes);
819
820 assertEquals(disconnectionSqlCodes, ds.getDisconnectionSqlCodes(), "Disconnection SQL codes should match the set values.");
821 assertEquals(disconnectionIgnoreSqlCodes, ds.getDisconnectionIgnoreSqlCodes(), "Disconnection Ignore SQL codes should match the set values.");
822 }
823
824 @Test
825 void testOverlapBetweenDisconnectionAndIgnoreSqlCodes() {
826
827 final HashSet<String> disconnectionSqlCodes = new HashSet<>(Arrays.asList("XXX", "ZZZ"));
828 ds.setDisconnectionSqlCodes(disconnectionSqlCodes);
829
830 final HashSet<String> disconnectionIgnoreSqlCodes = new HashSet<>(Arrays.asList("YYY", "XXX"));
831
832 final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
833 () -> ds.setDisconnectionIgnoreSqlCodes(disconnectionIgnoreSqlCodes));
834 assertEquals("[XXX] cannot be in both disconnectionSqlCodes and disconnectionIgnoreSqlCodes.", exception.getMessage());
835 }
836
837
838
839
840
841 @Test
842 void testPoolCloseCheckedException() throws Exception {
843 ds.setAccessToUnderlyingConnectionAllowed(true);
844
845 final TesterConnection tc;
846
847 try (Connection conn = ds.getConnection()) {
848 tc = (TesterConnection) ((DelegatingConnection<?>) conn).getInnermostDelegate();
849 }
850
851
852
853 tc.setFailure(new SQLException("bang"));
854
855
856
857
858 try {
859 StackMessageLog.lock();
860 StackMessageLog.clear();
861 ds.close();
862
863 final String message = StackMessageLog.popMessage();
864 Assertions.assertNotNull(message);
865 assertTrue(message.indexOf("bang") > 0);
866 } catch (final SQLException ex) {
867 assertTrue(ex.getMessage().indexOf("Cannot close") > 0);
868 assertTrue(ex.getCause().getMessage().indexOf("bang") > 0);
869 } finally {
870 StackMessageLog.unLock();
871 }
872 }
873
874 @Test
875 void testPoolCloseRTE() throws Exception {
876
877 ds.setAccessToUnderlyingConnectionAllowed(true);
878 final TesterConnection tc;
879 try (Connection conn = ds.getConnection()) {
880 tc = (TesterConnection) ((DelegatingConnection<?>) conn).getInnermostDelegate();
881 }
882 tc.setFailure(new IllegalStateException("boom"));
883 try {
884 StackMessageLog.lock();
885 StackMessageLog.clear();
886 ds.close();
887 final String message = StackMessageLog.popMessage();
888 Assertions.assertNotNull(message);
889 assertTrue(message.indexOf("boom") > 0);
890 } catch (final IllegalStateException ex) {
891 assertTrue(ex.getMessage().indexOf("boom") > 0);
892 } finally {
893 StackMessageLog.unLock();
894 }
895 }
896
897 @Override
898 @Test
899 public void testPooling() throws Exception {
900
901 ds.setAccessToUnderlyingConnectionAllowed(true);
902 super.testPooling();
903 }
904
905
906
907
908 @Test
909 void testPropertyTestOnReturn() throws Exception {
910 ds.setValidationQuery("select 1 from dual");
911 ds.setTestOnBorrow(false);
912 ds.setTestWhileIdle(false);
913 ds.setTestOnReturn(true);
914
915 try (Connection conn = ds.getConnection()) {
916 assertNotNull(conn);
917
918 assertFalse(ds.getConnectionPool().getTestOnBorrow());
919 assertFalse(ds.getConnectionPool().getTestWhileIdle());
920 assertTrue(ds.getConnectionPool().getTestOnReturn());
921 }
922 }
923
924 @Test
925 void testRestart() throws Exception {
926 ds.setMaxTotal(2);
927 ds.setDurationBetweenEvictionRuns(Duration.ofMillis(100));
928 ds.setNumTestsPerEvictionRun(2);
929 ds.setMinEvictableIdle(Duration.ofMinutes(1));
930 ds.setInitialSize(2);
931 ds.setDefaultCatalog("foo");
932 try (Connection conn1 = ds.getConnection()) {
933 Thread.sleep(200);
934
935 ds.setDefaultCatalog("bar");
936 ds.setInitialSize(1);
937
938 ds.restart();
939 assertEquals("bar", ds.getDefaultCatalog());
940 assertEquals(1, ds.getInitialSize());
941 ds.getLogWriter();
942 assertEquals(0, ds.getNumActive());
943 assertEquals(1, ds.getNumIdle());
944 }
945
946 assertEquals(1, ds.getNumIdle());
947 ds.close();
948 }
949
950
951
952
953 @Test
954 void testRollbackReadOnly() throws Exception {
955 ds.setDefaultReadOnly(Boolean.TRUE);
956 ds.setDefaultAutoCommit(Boolean.FALSE);
957
958 try (Connection conn = ds.getConnection()) {
959 assertNotNull(conn);
960 }
961 }
962
963 @Test
964 void testSetAutoCommitTrueOnClose() throws Exception {
965 ds.setAccessToUnderlyingConnectionAllowed(true);
966 ds.setDefaultAutoCommit(Boolean.FALSE);
967 final Connection dconn;
968 try (Connection conn = getConnection()) {
969 assertNotNull(conn);
970 assertFalse(conn.getAutoCommit());
971
972 dconn = ((DelegatingConnection<?>) conn).getInnermostDelegate();
973 assertNotNull(dconn);
974 assertFalse(dconn.getAutoCommit());
975
976 }
977
978 assertTrue(dconn.getAutoCommit());
979 }
980
981 @Test
982 void testSetProperties() throws Exception {
983
984 ds.setConnectionProperties("name1=value1;name2=value2;name3=value3");
985 assertEquals(3, ds.getConnectionProperties().size());
986 assertEquals("value1", ds.getConnectionProperties().getProperty("name1"));
987 assertEquals("value2", ds.getConnectionProperties().getProperty("name2"));
988 assertEquals("value3", ds.getConnectionProperties().getProperty("name3"));
989
990
991 ds.setConnectionProperties("name1=value1;name2=value2");
992 assertEquals(2, ds.getConnectionProperties().size());
993 assertEquals("value1", ds.getConnectionProperties().getProperty("name1"));
994 assertEquals("value2", ds.getConnectionProperties().getProperty("name2"));
995 assertFalse(ds.getConnectionProperties().containsKey("name3"));
996
997
998 ds.setConnectionProperties("name1=value1;name2");
999 assertEquals(2, ds.getConnectionProperties().size());
1000 assertEquals("value1", ds.getConnectionProperties().getProperty("name1"));
1001 assertEquals("", ds.getConnectionProperties().getProperty("name2"));
1002
1003
1004 ds.setConnectionProperties("name1=value1;name2=");
1005 assertEquals(2, ds.getConnectionProperties().size());
1006 assertEquals("value1", ds.getConnectionProperties().getProperty("name1"));
1007 assertEquals("", ds.getConnectionProperties().getProperty("name2"));
1008
1009
1010 ds.setConnectionProperties("name1=value1");
1011 assertEquals(1, ds.getConnectionProperties().size());
1012 assertEquals("value1", ds.getConnectionProperties().getProperty("name1"));
1013
1014
1015 ds.setConnectionProperties("name1=value1;");
1016 assertEquals(1, ds.getConnectionProperties().size());
1017 assertEquals("value1", ds.getConnectionProperties().getProperty("name1"));
1018
1019
1020 ds.setConnectionProperties("name1");
1021 assertEquals(1, ds.getConnectionProperties().size());
1022 assertEquals("", ds.getConnectionProperties().getProperty("name1"));
1023
1024
1025 assertThrows(NullPointerException.class, () -> ds.setConnectionProperties(null));
1026 }
1027
1028 @Test
1029 void testSetValidationTestProperties() {
1030
1031 assertTrue(ds.getTestOnBorrow());
1032 assertFalse(ds.getTestOnReturn());
1033 assertFalse(ds.getTestWhileIdle());
1034
1035 ds.setTestOnBorrow(true);
1036 ds.setTestOnReturn(true);
1037 ds.setTestWhileIdle(true);
1038 assertTrue(ds.getTestOnBorrow());
1039 assertTrue(ds.getTestOnReturn());
1040 assertTrue(ds.getTestWhileIdle());
1041
1042 ds.setTestOnBorrow(false);
1043 ds.setTestOnReturn(false);
1044 ds.setTestWhileIdle(false);
1045 assertFalse(ds.getTestOnBorrow());
1046 assertFalse(ds.getTestOnReturn());
1047 assertFalse(ds.getTestWhileIdle());
1048 }
1049
1050 @Test
1051 void testStart() throws Exception {
1052 ds.setAccessToUnderlyingConnectionAllowed(true);
1053 ds.setMaxTotal(2);
1054 final DelegatingConnection<?> conn1 = (DelegatingConnection<?>) ds.getConnection();
1055 final DelegatingConnection<?> conn2 = (DelegatingConnection<?>) ds.getConnection();
1056 final Connection inner1 = conn1.getInnermostDelegate();
1057 final Connection inner2 = conn2.getInnermostDelegate();
1058 assertFalse(inner2.isClosed());
1059 conn2.close();
1060 assertFalse(inner2.isClosed());
1061
1062 ds.close();
1063
1064 assertFalse(conn1.isClosed());
1065 assertTrue(inner2.isClosed());
1066 assertEquals(0, ds.getNumIdle());
1067
1068
1069 ds.start();
1070 final Connection conn3 = ds.getConnection();
1071 final Connection conn4 = ds.getConnection();
1072 conn3.close();
1073 conn4.close();
1074
1075
1076 conn1.close();
1077 assertTrue(inner1.isClosed());
1078 }
1079
1080 @Test
1081 void testStartInitializes() throws Exception {
1082 ds.setInitialSize(2);
1083
1084 assertEquals(0, ds.getNumIdle());
1085 assertNull(ds.getRegisteredJmxName());
1086
1087
1088 ds.start();
1089 assertEquals(2, ds.getNumIdle());
1090 assertNotNull(ds.getRegisteredJmxName());
1091 }
1092
1093 @Test
1094 void testTransactionIsolationBehavior() throws Exception {
1095 try (final Connection conn = getConnection()) {
1096 assertNotNull(conn);
1097 assertEquals(Connection.TRANSACTION_READ_COMMITTED, conn.getTransactionIsolation());
1098 conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
1099 }
1100
1101 final Connection conn2 = getConnection();
1102 assertEquals(Connection.TRANSACTION_READ_COMMITTED, conn2.getTransactionIsolation());
1103
1104 final Connection conn3 = getConnection();
1105 assertEquals(Connection.TRANSACTION_READ_COMMITTED, conn3.getTransactionIsolation());
1106
1107 conn2.close();
1108
1109 conn3.close();
1110 }
1111
1112 @Test
1113 void testUnwrap() throws Exception {
1114 assertSame(ds.unwrap(BasicDataSource.class), ds);
1115 assertSame(ds.unwrap(AutoCloseable.class), ds);
1116 assertThrows(SQLException.class, () -> ds.unwrap(String.class));
1117 assertThrows(SQLException.class, () -> ds.unwrap(null));
1118 }
1119
1120 @Test
1121 void testValidationQueryTimeoutNegative() throws Exception {
1122 ds.setTestOnBorrow(true);
1123 ds.setTestOnReturn(true);
1124 ds.setValidationQueryTimeout(Duration.ofSeconds(-1));
1125 try (final Connection con = ds.getConnection()) {
1126
1127 }
1128 }
1129
1130 @Test
1131 void testValidationQueryTimeoutSucceed() throws Exception {
1132 ds.setTestOnBorrow(true);
1133 ds.setTestOnReturn(true);
1134 ds.setValidationQueryTimeout(Duration.ofMillis(100));
1135 try (final Connection con = ds.getConnection()) {
1136
1137 }
1138 }
1139
1140 @Test
1141 void testValidationQueryTimeoutZero() throws Exception {
1142 ds.setTestOnBorrow(true);
1143 ds.setTestOnReturn(true);
1144 ds.setValidationQueryTimeout(Duration.ZERO);
1145 try (final Connection con = ds.getConnection()) {
1146
1147 }
1148 }
1149
1150 @Test
1151 void testValidationQueryTimoutFail() {
1152 ds.setTestOnBorrow(true);
1153 ds.setValidationQueryTimeout(Duration.ofSeconds(3));
1154 final SQLException e = assertThrows(SQLException.class, ds::getConnection);
1155 assertTrue(e.toString().contains("timeout"));
1156 }
1157 }
1158
1159
1160
1161
1162 final class TesterConnectionDelayDriver extends TesterDriver {
1163 private static final String CONNECT_STRING = "jdbc:apache:commons:testerConnectionDelayDriver";
1164
1165 public TesterConnectionDelayDriver() {
1166
1167 }
1168
1169 @Override
1170 public boolean acceptsURL(final String url) throws SQLException {
1171 return url.startsWith(CONNECT_STRING);
1172 }
1173
1174 @Override
1175 public Connection connect(final String url, final Properties info) throws SQLException {
1176 final String[] parsedUrl = url.split(":");
1177 final int delay = Integer.parseInt(parsedUrl[parsedUrl.length - 1]);
1178 try {
1179 Thread.sleep(delay);
1180 } catch (final InterruptedException ex) {
1181 Thread.currentThread().interrupt();
1182 }
1183 return super.connect(url, info);
1184 }
1185
1186 }
1187
1188
1189
1190
1191 final class TesterConnRequestCountDriver extends TesterDriver {
1192 private static final String CONNECT_STRING = "jdbc:apache:commons:testerConnRequestCountDriver";
1193 private static final AtomicInteger connectionRequestCount = new AtomicInteger();
1194
1195 public static int getConnectionRequestCount() {
1196 return connectionRequestCount.get();
1197 }
1198
1199 public static void initConnRequestCount() {
1200 connectionRequestCount.set(0);
1201 }
1202
1203 public TesterConnRequestCountDriver() {
1204
1205 }
1206
1207 @Override
1208 public boolean acceptsURL(final String url) throws SQLException {
1209 return CONNECT_STRING.startsWith(url);
1210 }
1211
1212 @Override
1213 public Connection connect(final String url, final Properties info) throws SQLException {
1214 connectionRequestCount.incrementAndGet();
1215 return super.connect(url, info);
1216 }
1217 }