1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.dbcp2.cpdsadapter;
18
19 import java.io.PrintWriter;
20 import java.io.Serializable;
21 import java.sql.DriverManager;
22 import java.sql.SQLException;
23 import java.sql.SQLFeatureNotSupportedException;
24 import java.time.Duration;
25 import java.util.Hashtable;
26 import java.util.Properties;
27 import java.util.logging.Logger;
28
29 import javax.naming.Context;
30 import javax.naming.Name;
31 import javax.naming.NamingException;
32 import javax.naming.RefAddr;
33 import javax.naming.Reference;
34 import javax.naming.Referenceable;
35 import javax.naming.StringRefAddr;
36 import javax.naming.spi.ObjectFactory;
37 import javax.sql.ConnectionPoolDataSource;
38 import javax.sql.PooledConnection;
39
40 import org.apache.commons.dbcp2.Constants;
41 import org.apache.commons.dbcp2.DelegatingPreparedStatement;
42 import org.apache.commons.dbcp2.PStmtKey;
43 import org.apache.commons.dbcp2.Utils;
44 import org.apache.commons.pool2.KeyedObjectPool;
45 import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
46 import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
47 import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public class DriverAdapterCPDS implements ConnectionPoolDataSource, Referenceable, Serializable, ObjectFactory {
80
81 private static final String KEY_MIN_EVICTABLE_IDLE_DURATION = "minEvictableIdleDuration";
82
83 private static final String KEY_DURATION_BETWEEN_EVICTION_RUNS = "durationBetweenEvictionRuns";
84
85 private static final String KEY_LOGIN_TIMEOUT = "loginTimeout";
86
87 private static final String KEY_URL = "url";
88
89 private static final String KEY_DRIVER = "driver";
90
91 private static final String KEY_DESCRIPTION = "description";
92
93 private static final String KEY_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED = "accessToUnderlyingConnectionAllowed";
94
95 private static final String KEY_MAX_PREPARED_STATEMENTS = "maxPreparedStatements";
96
97 private static final String KEY_MIN_EVICTABLE_IDLE_TIME_MILLIS = "minEvictableIdleTimeMillis";
98
99 private static final String KEY_NUM_TESTS_PER_EVICTION_RUN = "numTestsPerEvictionRun";
100
101 private static final String KEY_TIME_BETWEEN_EVICTION_RUNS_MILLIS = "timeBetweenEvictionRunsMillis";
102
103 private static final String KEY_MAX_IDLE = "maxIdle";
104
105 private static final String KEY_POOL_PREPARED_STATEMENTS = "poolPreparedStatements";
106
107 private static final long serialVersionUID = -4820523787212147844L;
108
109 private static final String GET_CONNECTION_CALLED = "A PooledConnection was already requested from this source, further initialization is not allowed.";
110
111 static {
112
113 DriverManager.getDrivers();
114 }
115
116
117 private String description;
118
119
120 private String connectionString;
121
122
123 private String userName;
124
125
126 private char[] userPassword;
127
128
129 private String driver;
130
131
132 private int loginTimeout;
133
134
135 private transient PrintWriter logWriter;
136
137
138 private boolean poolPreparedStatements;
139 private int maxIdle = 10;
140 private Duration durationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS;
141 private int numTestsPerEvictionRun = -1;
142 private Duration minEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION;
143
144 private int maxPreparedStatements = -1;
145
146
147 private volatile boolean getConnectionCalled;
148
149
150 private Properties connectionProperties;
151
152
153
154
155 private boolean accessToUnderlyingConnectionAllowed;
156
157
158
159
160 public DriverAdapterCPDS() {
161 }
162
163
164
165
166 private void assertInitializationAllowed() throws IllegalStateException {
167 if (getConnectionCalled) {
168 throw new IllegalStateException(GET_CONNECTION_CALLED);
169 }
170 }
171
172 private boolean getBooleanContentString(final RefAddr ra) {
173 return Boolean.parseBoolean(getStringContent(ra));
174 }
175
176
177
178
179
180
181 public Properties getConnectionProperties() {
182 return connectionProperties;
183 }
184
185
186
187
188
189
190
191
192 public String getDescription() {
193 return description;
194 }
195
196
197
198
199
200
201 public String getDriver() {
202 return driver;
203 }
204
205
206
207
208
209
210
211
212
213 public Duration getDurationBetweenEvictionRuns() {
214 return durationBetweenEvictionRuns;
215 }
216
217 private int getIntegerStringContent(final RefAddr ra) {
218 return Integer.parseInt(getStringContent(ra));
219 }
220
221
222
223
224
225 @Override
226 public int getLoginTimeout() {
227 return loginTimeout;
228 }
229
230
231
232
233 @Override
234 public PrintWriter getLogWriter() {
235 return logWriter;
236 }
237
238
239
240
241
242
243
244 public int getMaxIdle() {
245 return maxIdle;
246 }
247
248
249
250
251
252
253 public int getMaxPreparedStatements() {
254 return maxPreparedStatements;
255 }
256
257
258
259
260
261
262
263
264
265
266 public Duration getMinEvictableIdleDuration() {
267 return minEvictableIdleDuration;
268 }
269
270
271
272
273
274
275
276
277
278
279 @Deprecated
280 public int getMinEvictableIdleTimeMillis() {
281 return (int) minEvictableIdleDuration.toMillis();
282 }
283
284
285
286
287
288
289
290
291 public int getNumTestsPerEvictionRun() {
292 return numTestsPerEvictionRun;
293 }
294
295
296
297
298 @Override
299 public Object getObjectInstance(final Object refObj, final Name name, final Context context, final Hashtable<?, ?> env) throws ClassNotFoundException {
300
301
302 DriverAdapterCPDS cpds = null;
303 if (refObj instanceof Reference) {
304 final Reference ref = (Reference) refObj;
305 if (ref.getClassName().equals(getClass().getName())) {
306 RefAddr ra = ref.get(KEY_DESCRIPTION);
307 if (isNotEmpty(ra)) {
308 setDescription(getStringContent(ra));
309 }
310
311 ra = ref.get(KEY_DRIVER);
312 if (isNotEmpty(ra)) {
313 setDriver(getStringContent(ra));
314 }
315 ra = ref.get(KEY_URL);
316 if (isNotEmpty(ra)) {
317 setUrl(getStringContent(ra));
318 }
319 ra = ref.get(Constants.KEY_USER);
320 if (isNotEmpty(ra)) {
321 setUser(getStringContent(ra));
322 }
323 ra = ref.get(Constants.KEY_PASSWORD);
324 if (isNotEmpty(ra)) {
325 setPassword(getStringContent(ra));
326 }
327
328 ra = ref.get(KEY_POOL_PREPARED_STATEMENTS);
329 if (isNotEmpty(ra)) {
330 setPoolPreparedStatements(getBooleanContentString(ra));
331 }
332 ra = ref.get(KEY_MAX_IDLE);
333 if (isNotEmpty(ra)) {
334 setMaxIdle(getIntegerStringContent(ra));
335 }
336
337 ra = ref.get(KEY_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
338 if (isNotEmpty(ra)) {
339 setTimeBetweenEvictionRunsMillis(getIntegerStringContent(ra));
340 }
341
342 ra = ref.get(KEY_NUM_TESTS_PER_EVICTION_RUN);
343 if (isNotEmpty(ra)) {
344 setNumTestsPerEvictionRun(getIntegerStringContent(ra));
345 }
346
347 ra = ref.get(KEY_MIN_EVICTABLE_IDLE_TIME_MILLIS);
348 if (isNotEmpty(ra)) {
349 setMinEvictableIdleTimeMillis(getIntegerStringContent(ra));
350 }
351
352 ra = ref.get(KEY_MAX_PREPARED_STATEMENTS);
353 if (isNotEmpty(ra)) {
354 setMaxPreparedStatements(getIntegerStringContent(ra));
355 }
356
357 ra = ref.get(KEY_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED);
358 if (isNotEmpty(ra)) {
359 setAccessToUnderlyingConnectionAllowed(getBooleanContentString(ra));
360 }
361
362 cpds = this;
363 }
364 }
365 return cpds;
366 }
367
368 @Override
369 public Logger getParentLogger() throws SQLFeatureNotSupportedException {
370 throw new SQLFeatureNotSupportedException();
371 }
372
373
374
375
376
377
378 public String getPassword() {
379 return Utils.toString(userPassword);
380 }
381
382
383
384
385
386
387
388 public char[] getPasswordCharArray() {
389 return Utils.clone(userPassword);
390 }
391
392
393
394
395 @Override
396 public PooledConnection getPooledConnection() throws SQLException {
397 return getPooledConnection(getUser(), getPassword());
398 }
399
400
401
402
403
404
405
406 @Override
407 public PooledConnection getPooledConnection(final String pooledUserName, final String pooledUserPassword) throws SQLException {
408 getConnectionCalled = true;
409 if (connectionProperties != null) {
410 update(connectionProperties, Constants.KEY_USER, pooledUserName);
411 update(connectionProperties, Constants.KEY_PASSWORD, pooledUserPassword);
412 }
413
414 PooledConnectionImpl pooledConnection = null;
415 try {
416 pooledConnection = getPooledConnectionImpl(pooledUserName, pooledUserPassword);
417 } catch (final ClassCircularityError e) {
418 pooledConnection = getPooledConnectionImpl(pooledUserName, pooledUserPassword);
419 }
420 if (isPoolPreparedStatements()) {
421 final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
422 config.setMaxTotalPerKey(Integer.MAX_VALUE);
423 config.setBlockWhenExhausted(false);
424 config.setMaxWait(Duration.ZERO);
425 config.setMaxIdlePerKey(getMaxIdle());
426 if (getMaxPreparedStatements() <= 0) {
427
428
429 config.setTimeBetweenEvictionRuns(getDurationBetweenEvictionRuns());
430 config.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun());
431 config.setMinEvictableIdleDuration(getMinEvictableIdleDuration());
432 } else {
433
434
435
436 config.setMaxTotal(getMaxPreparedStatements());
437 config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
438 config.setNumTestsPerEvictionRun(0);
439 config.setMinEvictableIdleDuration(Duration.ZERO);
440 }
441 @SuppressWarnings("resource")
442 final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>(pooledConnection, config);
443 pooledConnection.setStatementPool(stmtPool);
444 }
445 return pooledConnection;
446 }
447
448 @SuppressWarnings("resource")
449 private PooledConnectionImpl getPooledConnectionImpl(final String pooledUserName, final String pooledUserPassword) throws SQLException {
450 PooledConnectionImpl pooledConnection;
451 if (connectionProperties != null) {
452 pooledConnection = new PooledConnectionImpl(DriverManager.getConnection(getUrl(), connectionProperties));
453 } else {
454 pooledConnection = new PooledConnectionImpl(DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword));
455 }
456 pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
457 return pooledConnection;
458 }
459
460
461
462
463 @Override
464 public Reference getReference() throws NamingException {
465
466 final String factory = getClass().getName();
467
468 final Reference ref = new Reference(getClass().getName(), factory, null);
469
470 ref.add(new StringRefAddr(KEY_DESCRIPTION, getDescription()));
471 ref.add(new StringRefAddr(KEY_DRIVER, getDriver()));
472 ref.add(new StringRefAddr(KEY_LOGIN_TIMEOUT, String.valueOf(getLoginTimeout())));
473 ref.add(new StringRefAddr(Constants.KEY_PASSWORD, getPassword()));
474 ref.add(new StringRefAddr(Constants.KEY_USER, getUser()));
475 ref.add(new StringRefAddr(KEY_URL, getUrl()));
476
477 ref.add(new StringRefAddr(KEY_POOL_PREPARED_STATEMENTS, String.valueOf(isPoolPreparedStatements())));
478 ref.add(new StringRefAddr(KEY_MAX_IDLE, String.valueOf(getMaxIdle())));
479 ref.add(new StringRefAddr(KEY_NUM_TESTS_PER_EVICTION_RUN, String.valueOf(getNumTestsPerEvictionRun())));
480 ref.add(new StringRefAddr(KEY_MAX_PREPARED_STATEMENTS, String.valueOf(getMaxPreparedStatements())));
481
482
483 ref.add(new StringRefAddr(KEY_DURATION_BETWEEN_EVICTION_RUNS, String.valueOf(getDurationBetweenEvictionRuns())));
484 ref.add(new StringRefAddr(KEY_TIME_BETWEEN_EVICTION_RUNS_MILLIS, String.valueOf(getTimeBetweenEvictionRunsMillis())));
485
486
487 ref.add(new StringRefAddr(KEY_MIN_EVICTABLE_IDLE_DURATION, String.valueOf(getMinEvictableIdleDuration())));
488 ref.add(new StringRefAddr(KEY_MIN_EVICTABLE_IDLE_TIME_MILLIS, String.valueOf(getMinEvictableIdleTimeMillis())));
489
490 return ref;
491 }
492
493 private String getStringContent(final RefAddr ra) {
494 return ra.getContent().toString();
495 }
496
497
498
499
500
501
502
503
504
505 @Deprecated
506 public long getTimeBetweenEvictionRunsMillis() {
507 return durationBetweenEvictionRuns.toMillis();
508 }
509
510
511
512
513
514
515 public String getUrl() {
516 return connectionString;
517 }
518
519
520
521
522
523
524 public String getUser() {
525 return userName;
526 }
527
528
529
530
531
532
533 public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
534 return this.accessToUnderlyingConnectionAllowed;
535 }
536
537 private boolean isNotEmpty(final RefAddr ra) {
538 return ra != null && ra.getContent() != null;
539 }
540
541
542
543
544
545
546 public boolean isPoolPreparedStatements() {
547 return poolPreparedStatements;
548 }
549
550
551
552
553
554
555
556 public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
557 this.accessToUnderlyingConnectionAllowed = allow;
558 }
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574 public void setConnectionProperties(final Properties props) {
575 assertInitializationAllowed();
576 connectionProperties = props;
577 if (connectionProperties != null) {
578 final String user = connectionProperties.getProperty(Constants.KEY_USER);
579 if (user != null) {
580 setUser(user);
581 }
582 final String password = connectionProperties.getProperty(Constants.KEY_PASSWORD);
583 if (password != null) {
584 setPassword(password);
585 }
586 }
587 }
588
589
590
591
592
593
594
595 public void setDescription(final String description) {
596 this.description = description;
597 }
598
599
600
601
602
603
604
605
606
607 public void setDriver(final String driver) throws ClassNotFoundException {
608 assertInitializationAllowed();
609 this.driver = driver;
610
611 Class.forName(driver);
612 }
613
614
615
616
617
618
619
620
621
622
623
624 public void setDurationBetweenEvictionRuns(final Duration durationBetweenEvictionRuns) {
625 assertInitializationAllowed();
626 this.durationBetweenEvictionRuns = durationBetweenEvictionRuns;
627 }
628
629
630
631
632
633 @Override
634 public void setLoginTimeout(final int seconds) {
635 this.loginTimeout = seconds;
636 }
637
638
639
640
641 @Override
642 public void setLogWriter(final PrintWriter logWriter) {
643 this.logWriter = logWriter;
644 }
645
646
647
648
649
650
651
652
653 public void setMaxIdle(final int maxIdle) {
654 assertInitializationAllowed();
655 this.maxIdle = maxIdle;
656 }
657
658
659
660
661
662
663 public void setMaxPreparedStatements(final int maxPreparedStatements) {
664 this.maxPreparedStatements = maxPreparedStatements;
665 }
666
667
668
669
670
671
672
673
674
675
676
677 public void setMinEvictableIdleDuration(final Duration minEvictableIdleDuration) {
678 assertInitializationAllowed();
679 this.minEvictableIdleDuration = minEvictableIdleDuration;
680 }
681
682
683
684
685
686
687
688
689
690
691
692 @Deprecated
693 public void setMinEvictableIdleTimeMillis(final int minEvictableIdleTimeMillis) {
694 assertInitializationAllowed();
695 this.minEvictableIdleDuration = Duration.ofMillis(minEvictableIdleTimeMillis);
696 }
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711 public void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
712 assertInitializationAllowed();
713 this.numTestsPerEvictionRun = numTestsPerEvictionRun;
714 }
715
716
717
718
719
720
721
722 public void setPassword(final char[] userPassword) {
723 assertInitializationAllowed();
724 this.userPassword = Utils.clone(userPassword);
725 update(connectionProperties, Constants.KEY_PASSWORD, Utils.toString(this.userPassword));
726 }
727
728
729
730
731
732
733
734 public void setPassword(final String userPassword) {
735 assertInitializationAllowed();
736 this.userPassword = Utils.toCharArray(userPassword);
737 update(connectionProperties, Constants.KEY_PASSWORD, userPassword);
738 }
739
740
741
742
743
744
745
746 public void setPoolPreparedStatements(final boolean poolPreparedStatements) {
747 assertInitializationAllowed();
748 this.poolPreparedStatements = poolPreparedStatements;
749 }
750
751
752
753
754
755
756
757
758
759
760
761 @Deprecated
762 public void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
763 assertInitializationAllowed();
764 this.durationBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis);
765 }
766
767
768
769
770
771
772
773 public void setUrl(final String connectionString) {
774 assertInitializationAllowed();
775 this.connectionString = connectionString;
776 }
777
778
779
780
781
782
783
784 public void setUser(final String userName) {
785 assertInitializationAllowed();
786 this.userName = userName;
787 update(connectionProperties, Constants.KEY_USER, userName);
788 }
789
790
791
792
793
794
795 @Override
796 public synchronized String toString() {
797 final StringBuilder builder = new StringBuilder(super.toString());
798 builder.append("[description=");
799 builder.append(description);
800 builder.append(", connectionString=");
801
802
803 builder.append(connectionString);
804 builder.append(", driver=");
805 builder.append(driver);
806 builder.append(", loginTimeout=");
807 builder.append(loginTimeout);
808 builder.append(", poolPreparedStatements=");
809 builder.append(poolPreparedStatements);
810 builder.append(", maxIdle=");
811 builder.append(maxIdle);
812 builder.append(", timeBetweenEvictionRunsMillis=");
813 builder.append(durationBetweenEvictionRuns);
814 builder.append(", numTestsPerEvictionRun=");
815 builder.append(numTestsPerEvictionRun);
816 builder.append(", minEvictableIdleTimeMillis=");
817 builder.append(minEvictableIdleDuration);
818 builder.append(", maxPreparedStatements=");
819 builder.append(maxPreparedStatements);
820 builder.append(", getConnectionCalled=");
821 builder.append(getConnectionCalled);
822 builder.append(", connectionProperties=");
823 builder.append(Utils.cloneWithoutCredentials(connectionProperties));
824 builder.append(", accessToUnderlyingConnectionAllowed=");
825 builder.append(accessToUnderlyingConnectionAllowed);
826 builder.append("]");
827 return builder.toString();
828 }
829
830 private void update(final Properties properties, final String key, final String value) {
831 if (properties != null && key != null) {
832 if (value == null) {
833 properties.remove(key);
834 } else {
835 properties.setProperty(key, value);
836 }
837 }
838 }
839 }