1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.ibatis.common.jdbc;
17
18 import com.ibatis.common.beans.ClassInfo;
19 import com.ibatis.common.logging.Log;
20 import com.ibatis.common.logging.LogFactory;
21 import com.ibatis.common.resources.Resources;
22
23 import java.io.PrintWriter;
24 import java.lang.reflect.InvocationHandler;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Proxy;
27 import java.sql.CallableStatement;
28 import java.sql.Connection;
29 import java.sql.DatabaseMetaData;
30 import java.sql.DriverManager;
31 import java.sql.PreparedStatement;
32 import java.sql.ResultSet;
33 import java.sql.SQLException;
34 import java.sql.SQLFeatureNotSupportedException;
35 import java.sql.SQLWarning;
36 import java.sql.Savepoint;
37 import java.sql.Statement;
38 import java.util.ArrayList;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Properties;
43 import java.util.logging.Logger;
44
45 import javax.sql.DataSource;
46
47
48
49
50
51
52
53
54
55 public class SimpleDataSource implements DataSource {
56
57
58 private static final Log log = LogFactory.getLog(SimpleDataSource.class);
59
60
61
62 private static final String PROP_JDBC_DRIVER = "JDBC.Driver";
63
64
65 private static final String PROP_JDBC_URL = "JDBC.ConnectionURL";
66
67
68 private static final String PROP_JDBC_USERNAME = "JDBC.Username";
69
70
71 private static final String PROP_JDBC_PASSWORD = "JDBC.Password";
72
73
74 private static final String PROP_JDBC_DEFAULT_AUTOCOMMIT = "JDBC.DefaultAutoCommit";
75
76
77
78 private static final String PROP_POOL_MAX_ACTIVE_CONN = "Pool.MaximumActiveConnections";
79
80
81 private static final String PROP_POOL_MAX_IDLE_CONN = "Pool.MaximumIdleConnections";
82
83
84 private static final String PROP_POOL_MAX_CHECKOUT_TIME = "Pool.MaximumCheckoutTime";
85
86
87 private static final String PROP_POOL_TIME_TO_WAIT = "Pool.TimeToWait";
88
89
90 private static final String PROP_POOL_PING_QUERY = "Pool.PingQuery";
91
92
93 private static final String PROP_POOL_PING_CONN_OLDER_THAN = "Pool.PingConnectionsOlderThan";
94
95
96 private static final String PROP_POOL_PING_ENABLED = "Pool.PingEnabled";
97
98
99 private static final String PROP_POOL_PING_CONN_NOT_USED_FOR = "Pool.PingConnectionsNotUsedFor";
100
101
102 private int expectedConnectionTypeCode;
103
104
105
106 private static final String ADD_DRIVER_PROPS_PREFIX = "Driver.";
107
108
109 private static final int ADD_DRIVER_PROPS_PREFIX_LENGTH = ADD_DRIVER_PROPS_PREFIX.length();
110
111
112
113 private final Object POOL_LOCK = new Object();
114
115
116 private List idleConnections = new ArrayList<>();
117
118
119 private List activeConnections = new ArrayList<>();
120
121
122 private long requestCount = 0;
123
124
125 private long accumulatedRequestTime = 0;
126
127
128 private long accumulatedCheckoutTime = 0;
129
130
131 private long claimedOverdueConnectionCount = 0;
132
133
134 private long accumulatedCheckoutTimeOfOverdueConnections = 0;
135
136
137 private long accumulatedWaitTime = 0;
138
139
140 private long hadToWaitCount = 0;
141
142
143 private long badConnectionCount = 0;
144
145
146
147
148 private String jdbcDriver;
149
150
151 private String jdbcUrl;
152
153
154 private String jdbcUsername;
155
156
157 private String jdbcPassword;
158
159
160 private boolean jdbcDefaultAutoCommit;
161
162
163 private Properties driverProps;
164
165
166 private boolean useDriverProps;
167
168
169 private int poolMaximumActiveConnections;
170
171
172 private int poolMaximumIdleConnections;
173
174
175 private int poolMaximumCheckoutTime;
176
177
178 private int poolTimeToWait;
179
180
181 private String poolPingQuery;
182
183
184 private boolean poolPingEnabled;
185
186
187 private int poolPingConnectionsOlderThan;
188
189
190 private int poolPingConnectionsNotUsedFor;
191
192
193
194
195
196
197
198
199
200 public SimpleDataSource(Map props) {
201 initialize(props);
202 }
203
204
205
206
207
208
209
210 private void initialize(Map props) {
211 try {
212 String prop_pool_ping_query = null;
213
214 if (props == null) {
215 throw new RuntimeException("SimpleDataSource: The properties map passed to the initializer was null.");
216 }
217
218 if (!props.containsKey(PROP_JDBC_DRIVER) || !props.containsKey(PROP_JDBC_URL)
219 || !props.containsKey(PROP_JDBC_USERNAME) || !props.containsKey(PROP_JDBC_PASSWORD)) {
220 throw new RuntimeException("SimpleDataSource: Some properties were not set.");
221 }
222 jdbcDriver = (String) props.get(PROP_JDBC_DRIVER);
223 jdbcUrl = (String) props.get(PROP_JDBC_URL);
224 jdbcUsername = (String) props.get(PROP_JDBC_USERNAME);
225 jdbcPassword = (String) props.get(PROP_JDBC_PASSWORD);
226
227 poolMaximumActiveConnections = props.containsKey(PROP_POOL_MAX_ACTIVE_CONN)
228 ? Integer.parseInt((String) props.get(PROP_POOL_MAX_ACTIVE_CONN)) : 10;
229
230 poolMaximumIdleConnections = props.containsKey(PROP_POOL_MAX_IDLE_CONN)
231 ? Integer.parseInt((String) props.get(PROP_POOL_MAX_IDLE_CONN)) : 5;
232
233 poolMaximumCheckoutTime = props.containsKey(PROP_POOL_MAX_CHECKOUT_TIME)
234 ? Integer.parseInt((String) props.get(PROP_POOL_MAX_CHECKOUT_TIME)) : 20000;
235
236 poolTimeToWait = props.containsKey(PROP_POOL_TIME_TO_WAIT)
237 ? Integer.parseInt((String) props.get(PROP_POOL_TIME_TO_WAIT)) : 20000;
238
239 poolPingEnabled = props.containsKey(PROP_POOL_PING_ENABLED)
240 && Boolean.parseBoolean((String) props.get(PROP_POOL_PING_ENABLED));
241
242 prop_pool_ping_query = (String) props.get(PROP_POOL_PING_QUERY);
243 poolPingQuery = props.containsKey(PROP_POOL_PING_QUERY) ? prop_pool_ping_query : "NO PING QUERY SET";
244
245 poolPingConnectionsOlderThan = props.containsKey(PROP_POOL_PING_CONN_OLDER_THAN)
246 ? Integer.parseInt((String) props.get(PROP_POOL_PING_CONN_OLDER_THAN)) : 0;
247
248 poolPingConnectionsNotUsedFor = props.containsKey(PROP_POOL_PING_CONN_NOT_USED_FOR)
249 ? Integer.parseInt((String) props.get(PROP_POOL_PING_CONN_NOT_USED_FOR)) : 0;
250
251 jdbcDefaultAutoCommit = props.containsKey(PROP_JDBC_DEFAULT_AUTOCOMMIT)
252 && Boolean.parseBoolean((String) props.get(PROP_JDBC_DEFAULT_AUTOCOMMIT));
253
254 useDriverProps = false;
255 Iterator propIter = props.keySet().iterator();
256 driverProps = new Properties();
257 driverProps.put("user", jdbcUsername);
258 driverProps.put("password", jdbcPassword);
259 while (propIter.hasNext()) {
260 String name = (String) propIter.next();
261 String value = (String) props.get(name);
262 if (name.startsWith(ADD_DRIVER_PROPS_PREFIX)) {
263 driverProps.put(name.substring(ADD_DRIVER_PROPS_PREFIX_LENGTH), value);
264 useDriverProps = true;
265 }
266 }
267
268 expectedConnectionTypeCode = assembleConnectionTypeCode(jdbcUrl, jdbcUsername, jdbcPassword);
269
270 Resources.instantiate(jdbcDriver);
271
272 if (poolPingEnabled && (!props.containsKey(PROP_POOL_PING_QUERY) || prop_pool_ping_query.trim().length() == 0)) {
273 throw new RuntimeException("SimpleDataSource: property '" + PROP_POOL_PING_ENABLED + "' is true, but property '"
274 + PROP_POOL_PING_QUERY + "' is not set correctly.");
275 }
276
277 } catch (Exception e) {
278 log.error("SimpleDataSource: Error while loading properties. Cause: " + e.toString(), e);
279 throw new RuntimeException("SimpleDataSource: Error while loading properties. Cause: " + e, e);
280 }
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294
295 private int assembleConnectionTypeCode(String url, String username, String password) {
296 return ("" + url + username + password).hashCode();
297 }
298
299
300
301
302 @Override
303 public Connection getConnection() throws SQLException {
304 return popConnection(jdbcUsername, jdbcPassword).getProxyConnection();
305 }
306
307
308
309
310 @Override
311 public Connection getConnection(String username, String password) throws SQLException {
312 return popConnection(username, password).getProxyConnection();
313 }
314
315
316
317
318 @Override
319 public void setLoginTimeout(int loginTimeout) throws SQLException {
320 DriverManager.setLoginTimeout(loginTimeout);
321 }
322
323
324
325
326 @Override
327 public int getLoginTimeout() throws SQLException {
328 return DriverManager.getLoginTimeout();
329 }
330
331
332
333
334 @Override
335 public void setLogWriter(PrintWriter logWriter) throws SQLException {
336 DriverManager.setLogWriter(logWriter);
337 }
338
339
340
341
342 @Override
343 public PrintWriter getLogWriter() throws SQLException {
344 return DriverManager.getLogWriter();
345 }
346
347
348
349
350
351
352
353 public int getPoolPingConnectionsNotUsedFor() {
354 return poolPingConnectionsNotUsedFor;
355 }
356
357
358
359
360
361
362 public String getJdbcDriver() {
363 return jdbcDriver;
364 }
365
366
367
368
369
370
371 public String getJdbcUrl() {
372 return jdbcUrl;
373 }
374
375
376
377
378
379
380 public String getJdbcUsername() {
381 return jdbcUsername;
382 }
383
384
385
386
387
388
389 public String getJdbcPassword() {
390 return jdbcPassword;
391 }
392
393
394
395
396
397
398 public int getPoolMaximumActiveConnections() {
399 return poolMaximumActiveConnections;
400 }
401
402
403
404
405
406
407 public int getPoolMaximumIdleConnections() {
408 return poolMaximumIdleConnections;
409 }
410
411
412
413
414
415
416 public int getPoolMaximumCheckoutTime() {
417 return poolMaximumCheckoutTime;
418 }
419
420
421
422
423
424
425 public int getPoolTimeToWait() {
426 return poolTimeToWait;
427 }
428
429
430
431
432
433
434 public String getPoolPingQuery() {
435 return poolPingQuery;
436 }
437
438
439
440
441
442
443 public boolean isPoolPingEnabled() {
444 return poolPingEnabled;
445 }
446
447
448
449
450
451
452 public int getPoolPingConnectionsOlderThan() {
453 return poolPingConnectionsOlderThan;
454 }
455
456
457
458
459
460
461 private int getExpectedConnectionTypeCode() {
462 return expectedConnectionTypeCode;
463 }
464
465
466
467
468
469
470 public long getRequestCount() {
471 synchronized (POOL_LOCK) {
472 return requestCount;
473 }
474 }
475
476
477
478
479
480
481 public long getAverageRequestTime() {
482 synchronized (POOL_LOCK) {
483 return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount;
484 }
485 }
486
487
488
489
490
491
492 public long getAverageWaitTime() {
493 synchronized (POOL_LOCK) {
494 return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount;
495 }
496 }
497
498
499
500
501
502
503 public long getHadToWaitCount() {
504 synchronized (POOL_LOCK) {
505 return hadToWaitCount;
506 }
507 }
508
509
510
511
512
513
514 public long getBadConnectionCount() {
515 synchronized (POOL_LOCK) {
516 return badConnectionCount;
517 }
518 }
519
520
521
522
523
524
525 public long getClaimedOverdueConnectionCount() {
526 synchronized (POOL_LOCK) {
527 return claimedOverdueConnectionCount;
528 }
529 }
530
531
532
533
534
535
536 public long getAverageOverdueCheckoutTime() {
537 synchronized (POOL_LOCK) {
538 return claimedOverdueConnectionCount == 0 ? 0
539 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount;
540 }
541 }
542
543
544
545
546
547
548 public long getAverageCheckoutTime() {
549 synchronized (POOL_LOCK) {
550 return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount;
551 }
552 }
553
554
555
556
557
558
559 public String getStatus() {
560 StringBuilder builder = new StringBuilder();
561
562 builder.append("\n===============================================================");
563 builder.append("\n jdbcDriver ").append(jdbcDriver);
564 builder.append("\n jdbcUrl ").append(jdbcUrl);
565 builder.append("\n jdbcUsername ").append(jdbcUsername);
566 builder.append("\n jdbcPassword ").append(jdbcPassword == null ? "NULL" : "************");
567 builder.append("\n poolMaxActiveConnections ").append(poolMaximumActiveConnections);
568 builder.append("\n poolMaxIdleConnections ").append(poolMaximumIdleConnections);
569 builder.append("\n poolMaxCheckoutTime " + poolMaximumCheckoutTime);
570 builder.append("\n poolTimeToWait " + poolTimeToWait);
571 builder.append("\n poolPingEnabled " + poolPingEnabled);
572 builder.append("\n poolPingQuery " + poolPingQuery);
573 builder.append("\n poolPingConnectionsOlderThan " + poolPingConnectionsOlderThan);
574 builder.append("\n poolPingConnectionsNotUsedFor " + poolPingConnectionsNotUsedFor);
575 builder.append("\n --------------------------------------------------------------");
576 builder.append("\n activeConnections " + activeConnections.size());
577 builder.append("\n idleConnections " + idleConnections.size());
578 builder.append("\n requestCount " + getRequestCount());
579 builder.append("\n averageRequestTime " + getAverageRequestTime());
580 builder.append("\n averageCheckoutTime " + getAverageCheckoutTime());
581 builder.append("\n claimedOverdue " + getClaimedOverdueConnectionCount());
582 builder.append("\n averageOverdueCheckoutTime " + getAverageOverdueCheckoutTime());
583 builder.append("\n hadToWait " + getHadToWaitCount());
584 builder.append("\n averageWaitTime " + getAverageWaitTime());
585 builder.append("\n badConnectionCount " + getBadConnectionCount());
586 builder.append("\n===============================================================");
587 return builder.toString();
588 }
589
590
591
592
593 public void forceCloseAll() {
594 synchronized (POOL_LOCK) {
595 for (int i = activeConnections.size(); i > 0; i--) {
596 try {
597 SimplePooledConnection conn = (SimplePooledConnection) activeConnections.remove(i - 1);
598 conn.invalidate();
599
600 Connection realConn = conn.getRealConnection();
601 if (!realConn.getAutoCommit()) {
602 realConn.rollback();
603 }
604 realConn.close();
605 } catch (Exception e) {
606
607 }
608 }
609 for (int i = idleConnections.size(); i > 0; i--) {
610 try {
611 SimplePooledConnection conn = (SimplePooledConnection) idleConnections.remove(i - 1);
612 conn.invalidate();
613
614 Connection realConn = conn.getRealConnection();
615 if (!realConn.getAutoCommit()) {
616 realConn.rollback();
617 }
618 realConn.close();
619 } catch (Exception e) {
620
621 }
622 }
623 }
624 if (log.isDebugEnabled()) {
625 log.debug("SimpleDataSource forcefully closed/removed all connections.");
626 }
627 }
628
629
630
631
632
633
634
635
636
637
638 private void pushConnection(SimplePooledConnection conn) throws SQLException {
639
640 synchronized (POOL_LOCK) {
641 activeConnections.remove(conn);
642 if (conn.isValid()) {
643 if (idleConnections.size() < poolMaximumIdleConnections
644 && conn.getConnectionTypeCode() == getExpectedConnectionTypeCode()) {
645 accumulatedCheckoutTime += conn.getCheckoutTime();
646 if (!conn.getRealConnection().getAutoCommit()) {
647 conn.getRealConnection().rollback();
648 }
649 SimplePooledConnection newConn = new SimplePooledConnection(conn.getRealConnection(), this);
650 idleConnections.add(newConn);
651 newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
652 newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
653 conn.invalidate();
654 if (log.isDebugEnabled()) {
655 log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
656 }
657 POOL_LOCK.notifyAll();
658 } else {
659 accumulatedCheckoutTime += conn.getCheckoutTime();
660 if (!conn.getRealConnection().getAutoCommit()) {
661 conn.getRealConnection().rollback();
662 }
663 conn.getRealConnection().close();
664 if (log.isDebugEnabled()) {
665 log.debug("Closed connection " + conn.getRealHashCode() + ".");
666 }
667 conn.invalidate();
668 }
669 } else {
670 if (log.isDebugEnabled()) {
671 log.debug("A bad connection (" + conn.getRealHashCode()
672 + ") attempted to return to the pool, discarding connection.");
673 }
674 badConnectionCount++;
675 }
676 }
677 }
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692 private SimplePooledConnection popConnection(String username, String password) throws SQLException {
693 boolean countedWait = false;
694 SimplePooledConnection conn = null;
695 long t = System.currentTimeMillis();
696 int localBadConnectionCount = 0;
697
698 while (conn == null) {
699 synchronized (POOL_LOCK) {
700 if (!idleConnections.isEmpty()) {
701
702 conn = (SimplePooledConnection) idleConnections.remove(0);
703 if (log.isDebugEnabled()) {
704 log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
705 }
706 } else {
707
708 if (activeConnections.size() < poolMaximumActiveConnections) {
709
710 if (useDriverProps) {
711 conn = new SimplePooledConnection(DriverManager.getConnection(jdbcUrl, driverProps), this);
712 } else {
713 conn = new SimplePooledConnection(DriverManager.getConnection(jdbcUrl, jdbcUsername, jdbcPassword), this);
714 }
715 Connection realConn = conn.getRealConnection();
716 if (realConn.getAutoCommit() != jdbcDefaultAutoCommit) {
717 realConn.setAutoCommit(jdbcDefaultAutoCommit);
718 }
719 if (log.isDebugEnabled()) {
720 log.debug("Created connection " + conn.getRealHashCode() + ".");
721 }
722 } else {
723
724 SimplePooledConnection oldestActiveConnection = (SimplePooledConnection) activeConnections.get(0);
725 long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
726 if (longestCheckoutTime > poolMaximumCheckoutTime) {
727
728 claimedOverdueConnectionCount++;
729 accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
730 accumulatedCheckoutTime += longestCheckoutTime;
731 activeConnections.remove(oldestActiveConnection);
732 if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
733 oldestActiveConnection.getRealConnection().rollback();
734 }
735 conn = new SimplePooledConnection(oldestActiveConnection.getRealConnection(), this);
736 oldestActiveConnection.invalidate();
737 if (log.isDebugEnabled()) {
738 log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
739 }
740 } else {
741
742 try {
743 if (!countedWait) {
744 hadToWaitCount++;
745 countedWait = true;
746 }
747 if (log.isDebugEnabled()) {
748 log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
749 }
750 long wt = System.currentTimeMillis();
751 POOL_LOCK.wait(poolTimeToWait);
752 accumulatedWaitTime += System.currentTimeMillis() - wt;
753 } catch (InterruptedException e) {
754 break;
755 }
756 }
757 }
758 }
759 if (conn != null) {
760 if (conn.isValid()) {
761 if (!conn.getRealConnection().getAutoCommit()) {
762 conn.getRealConnection().rollback();
763 }
764 conn.setConnectionTypeCode(assembleConnectionTypeCode(jdbcUrl, username, password));
765 conn.setCheckoutTimestamp(System.currentTimeMillis());
766 conn.setLastUsedTimestamp(System.currentTimeMillis());
767 activeConnections.add(conn);
768 requestCount++;
769 accumulatedRequestTime += System.currentTimeMillis() - t;
770 } else {
771 if (log.isDebugEnabled()) {
772 log.debug("A bad connection (" + conn.getRealHashCode()
773 + ") was returned from the pool, getting another connection.");
774 }
775 badConnectionCount++;
776 localBadConnectionCount++;
777 conn = null;
778 if (localBadConnectionCount > poolMaximumIdleConnections + 3) {
779 if (log.isDebugEnabled()) {
780 log.debug("SimpleDataSource: Could not get a good connection to the database.");
781 }
782 throw new SQLException("SimpleDataSource: Could not get a good connection to the database.");
783 }
784 }
785 }
786 }
787
788 }
789
790 if (conn == null) {
791 if (log.isDebugEnabled()) {
792 log.debug("SimpleDataSource: Unknown severe error condition. The connection pool returned a null connection.");
793 }
794 throw new SQLException(
795 "SimpleDataSource: Unknown severe error condition. The connection pool returned a null connection.");
796 }
797
798 return conn;
799 }
800
801
802
803
804
805
806
807
808
809 private boolean pingConnection(SimplePooledConnection conn) {
810 boolean result = true;
811
812 try {
813 result = !conn.getRealConnection().isClosed();
814 } catch (SQLException e) {
815 if (log.isDebugEnabled()) {
816 log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
817 }
818 result = false;
819 }
820
821 if (result && poolPingEnabled) {
822 if (poolPingConnectionsOlderThan > 0 && conn.getAge() > poolPingConnectionsOlderThan
823 || poolPingConnectionsNotUsedFor > 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
824
825 try {
826 if (log.isDebugEnabled()) {
827 log.debug("Testing connection " + conn.getRealHashCode() + " ...");
828 }
829 Connection realConn = conn.getRealConnection();
830 Statement statement = realConn.createStatement();
831 ResultSet rs = statement.executeQuery(poolPingQuery);
832 rs.close();
833 statement.close();
834 if (!realConn.getAutoCommit()) {
835 realConn.rollback();
836 }
837 result = true;
838 if (log.isDebugEnabled()) {
839 log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
840 }
841 } catch (Exception e) {
842 log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
843 try {
844 conn.getRealConnection().close();
845 } catch (Exception e2) {
846
847 }
848 result = false;
849 if (log.isDebugEnabled()) {
850 log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
851 }
852 }
853 }
854 }
855 return result;
856 }
857
858
859
860
861
862
863
864
865
866 public static Connection unwrapConnection(Connection conn) {
867 if (conn instanceof SimplePooledConnection) {
868 return ((SimplePooledConnection) conn).getRealConnection();
869 }
870 return conn;
871 }
872
873 @Override
874 protected void finalize() throws Throwable {
875 forceCloseAll();
876 }
877
878
879
880
881
882 public static class SimplePooledConnection implements InvocationHandler {
883
884
885 private static final String CLOSE = "close";
886
887
888 private static final Class[] IFACES = { Connection.class };
889
890
891 private int hashCode = 0;
892
893
894 private SimpleDataSource dataSource;
895
896
897 private Connection realConnection;
898
899
900 private Connection proxyConnection;
901
902
903 private long checkoutTimestamp;
904
905
906 private long createdTimestamp;
907
908
909 private long lastUsedTimestamp;
910
911
912 private int connectionTypeCode;
913
914
915 private boolean valid;
916
917
918
919
920
921
922
923
924
925 public SimplePooledConnection(Connection connection, SimpleDataSource dataSource) {
926 this.hashCode = connection.hashCode();
927 this.realConnection = connection;
928 this.dataSource = dataSource;
929 this.createdTimestamp = System.currentTimeMillis();
930 this.lastUsedTimestamp = System.currentTimeMillis();
931 this.valid = true;
932
933 proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
934 }
935
936
937
938
939 public void invalidate() {
940 valid = false;
941 }
942
943
944
945
946
947
948 public boolean isValid() {
949 return valid && realConnection != null && dataSource.pingConnection(this);
950 }
951
952
953
954
955
956
957 public Connection getRealConnection() {
958 return realConnection;
959 }
960
961
962
963
964
965
966 public Connection getProxyConnection() {
967 return proxyConnection;
968 }
969
970
971
972
973
974
975 public int getRealHashCode() {
976 if (realConnection == null) {
977 return 0;
978 }
979 return realConnection.hashCode();
980 }
981
982
983
984
985
986
987 public int getConnectionTypeCode() {
988 return connectionTypeCode;
989 }
990
991
992
993
994
995
996
997 public void setConnectionTypeCode(int connectionTypeCode) {
998 this.connectionTypeCode = connectionTypeCode;
999 }
1000
1001
1002
1003
1004
1005
1006 public long getCreatedTimestamp() {
1007 return createdTimestamp;
1008 }
1009
1010
1011
1012
1013
1014
1015
1016 public void setCreatedTimestamp(long createdTimestamp) {
1017 this.createdTimestamp = createdTimestamp;
1018 }
1019
1020
1021
1022
1023
1024
1025 public long getLastUsedTimestamp() {
1026 return lastUsedTimestamp;
1027 }
1028
1029
1030
1031
1032
1033
1034
1035 public void setLastUsedTimestamp(long lastUsedTimestamp) {
1036 this.lastUsedTimestamp = lastUsedTimestamp;
1037 }
1038
1039
1040
1041
1042
1043
1044 public long getTimeElapsedSinceLastUse() {
1045 return System.currentTimeMillis() - lastUsedTimestamp;
1046 }
1047
1048
1049
1050
1051
1052
1053 public long getAge() {
1054 return System.currentTimeMillis() - createdTimestamp;
1055 }
1056
1057
1058
1059
1060
1061
1062 public long getCheckoutTimestamp() {
1063 return checkoutTimestamp;
1064 }
1065
1066
1067
1068
1069
1070
1071
1072 public void setCheckoutTimestamp(long timestamp) {
1073 this.checkoutTimestamp = timestamp;
1074 }
1075
1076
1077
1078
1079
1080
1081 public long getCheckoutTime() {
1082 return System.currentTimeMillis() - checkoutTimestamp;
1083 }
1084
1085
1086
1087
1088
1089
1090 private Connection getValidConnection() {
1091 if (!valid) {
1092 throw new RuntimeException("Error accessing SimplePooledConnection. Connection is invalid.");
1093 }
1094 return realConnection;
1095 }
1096
1097 @Override
1098 public int hashCode() {
1099 return hashCode;
1100 }
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110 @Override
1111 public boolean equals(Object obj) {
1112 if (obj instanceof SimplePooledConnection) {
1113 return realConnection.hashCode() == ((SimplePooledConnection) obj).realConnection.hashCode();
1114 }
1115 if (obj instanceof Connection) {
1116 return hashCode == obj.hashCode();
1117 }
1118 return false;
1119 }
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137 @Override
1138 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
1139 String methodName = method.getName();
1140 if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
1141 dataSource.pushConnection(this);
1142 return null;
1143 }
1144 try {
1145 return method.invoke(getValidConnection(), args);
1146 } catch (Throwable t) {
1147 throw ClassInfo.unwrapThrowable(t);
1148 }
1149 }
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159 public Statement createStatement() throws SQLException {
1160 return getValidConnection().createStatement();
1161 }
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174 public PreparedStatement prepareStatement(String sql) throws SQLException {
1175 return getValidConnection().prepareStatement(sql);
1176 }
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189 public CallableStatement prepareCall(String sql) throws SQLException {
1190 return getValidConnection().prepareCall(sql);
1191 }
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204 public String nativeSQL(String sql) throws SQLException {
1205 return getValidConnection().nativeSQL(sql);
1206 }
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217 public void setAutoCommit(boolean autoCommit) throws SQLException {
1218 getValidConnection().setAutoCommit(autoCommit);
1219 }
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229 public boolean getAutoCommit() throws SQLException {
1230 return getValidConnection().getAutoCommit();
1231 }
1232
1233
1234
1235
1236
1237
1238
1239 public void commit() throws SQLException {
1240 getValidConnection().commit();
1241 }
1242
1243
1244
1245
1246
1247
1248
1249 public void rollback() throws SQLException {
1250 getValidConnection().rollback();
1251 }
1252
1253
1254
1255
1256
1257
1258
1259 public void close() throws SQLException {
1260 dataSource.pushConnection(this);
1261 }
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271 public boolean isClosed() throws SQLException {
1272 return getValidConnection().isClosed();
1273 }
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283 public DatabaseMetaData getMetaData() throws SQLException {
1284 return getValidConnection().getMetaData();
1285 }
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296 public void setReadOnly(boolean readOnly) throws SQLException {
1297 getValidConnection().setReadOnly(readOnly);
1298 }
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308 public boolean isReadOnly() throws SQLException {
1309 return getValidConnection().isReadOnly();
1310 }
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321 public void setCatalog(String catalog) throws SQLException {
1322 getValidConnection().setCatalog(catalog);
1323 }
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333 public String getCatalog() throws SQLException {
1334 return getValidConnection().getCatalog();
1335 }
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346 public void setTransactionIsolation(int level) throws SQLException {
1347 getValidConnection().setTransactionIsolation(level);
1348 }
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358 public int getTransactionIsolation() throws SQLException {
1359 return getValidConnection().getTransactionIsolation();
1360 }
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370 public SQLWarning getWarnings() throws SQLException {
1371 return getValidConnection().getWarnings();
1372 }
1373
1374
1375
1376
1377
1378
1379
1380 public void clearWarnings() throws SQLException {
1381 getValidConnection().clearWarnings();
1382 }
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397 public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
1398 return getValidConnection().createStatement(resultSetType, resultSetConcurrency);
1399 }
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416 public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
1417 throws SQLException {
1418 return getValidConnection().prepareCall(sql, resultSetType, resultSetConcurrency);
1419 }
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
1437 return getValidConnection().prepareCall(sql, resultSetType, resultSetConcurrency);
1438 }
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448 public Map getTypeMap() throws SQLException {
1449 return getValidConnection().getTypeMap();
1450 }
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461 public void setTypeMap(Map map) throws SQLException {
1462 getValidConnection().setTypeMap(map);
1463 }
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478 public void setHoldability(int holdability) throws SQLException {
1479 getValidConnection().setHoldability(holdability);
1480 }
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490 public int getHoldability() throws SQLException {
1491 return getValidConnection().getHoldability();
1492 }
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502 public Savepoint setSavepoint() throws SQLException {
1503 return getValidConnection().setSavepoint();
1504 }
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517 public Savepoint setSavepoint(String name) throws SQLException {
1518 return getValidConnection().setSavepoint(name);
1519 }
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530 public void rollback(Savepoint savepoint) throws SQLException {
1531 getValidConnection().rollback(savepoint);
1532 }
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543 public void releaseSavepoint(Savepoint savepoint) throws SQLException {
1544 getValidConnection().releaseSavepoint(savepoint);
1545 }
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562 public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
1563 throws SQLException {
1564 return getValidConnection().createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
1565 }
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584 public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
1585 int resultSetHoldability) throws SQLException {
1586 return getValidConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
1587 }
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
1607 int resultSetHoldability) throws SQLException {
1608 return getValidConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
1609 }
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
1625 return getValidConnection().prepareStatement(sql, autoGeneratedKeys);
1626 }
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641 public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException {
1642 return getValidConnection().prepareStatement(sql, columnIndexes);
1643 }
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658 public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException {
1659 return getValidConnection().prepareStatement(sql, columnNames);
1660 }
1661
1662 }
1663
1664 @Override
1665 public Logger getParentLogger() throws SQLFeatureNotSupportedException {
1666
1667 return null;
1668 }
1669
1670 @Override
1671 public <T> T unwrap(Class<T> iface) throws SQLException {
1672
1673 return null;
1674 }
1675
1676 @Override
1677 public boolean isWrapperFor(Class<?> iface) throws SQLException {
1678
1679 return false;
1680 }
1681 }