1
1
package javaxt .sql ;
2
2
3
+ import java .util .Map ;
4
+ import java .util .HashMap ;
3
5
import java .io .PrintWriter ;
4
6
import java .sql .SQLException ;
5
7
import javax .sql .ConnectionEvent ;
@@ -45,93 +47,119 @@ public class ConnectionPool {
45
47
private final ConcurrentLinkedQueue <PooledConnectionWrapper > recycledConnections = new ConcurrentLinkedQueue <>();
46
48
private final ConcurrentHashMap <PooledConnection , PooledConnectionWrapper > connectionWrappers = new ConcurrentHashMap <>();
47
49
private volatile PooledConnection connectionInTransition ; // a PooledConnection which is currently within a PooledConnection.getConnection() call, or null
48
- private Database database ;
49
50
50
51
// Validation caching for performance optimization
51
52
private final ConcurrentHashMap <PooledConnection , Long > validationCache = new ConcurrentHashMap <>();
52
53
private static final long VALIDATION_CACHE_TTL = 30000 ; // 30 seconds
53
54
54
55
56
+ private Database database ;
57
+
58
+
55
59
/**
56
- * Thrown in {@link #getConnection()} or {@link #getValidConnection()} when no free connection becomes
60
+ * Thrown in {@link #getConnection()} when no free connection becomes
57
61
* available within <code>timeout</code> seconds.
58
62
*/
59
63
public static class TimeoutException extends RuntimeException {
60
- private static final long serialVersionUID = 1 ;
61
- public TimeoutException () {
62
- super ("Timeout while waiting for a free database connection." ); }
63
- public TimeoutException (String msg ) {
64
- super (msg );
65
- }
64
+ private static final long serialVersionUID = 1 ;
65
+ public TimeoutException () { super ("Timeout while waiting for a free database connection." ); }
66
+ public TimeoutException (String msg ) { super (msg ); }
66
67
}
67
68
68
69
69
70
//**************************************************************************
70
71
//** Constructor
71
72
//**************************************************************************
72
73
public ConnectionPool (Database database , int maxConnections ) throws SQLException {
73
- this (database == null ? null : database .getConnectionPoolDataSource (), maxConnections );
74
- if (database == null ) throw new IllegalArgumentException ("database is required" );
75
- this .database = database ;
74
+ this (database , maxConnections , null );
76
75
}
77
76
78
77
79
78
//**************************************************************************
80
79
//** Constructor
81
80
//**************************************************************************
82
81
public ConnectionPool (Database database , int maxConnections , int timeout ) throws SQLException {
83
- this (database == null ? null : database .getConnectionPoolDataSource (), maxConnections , timeout );
84
- if (database == null ) throw new IllegalArgumentException ("database is required" );
82
+ this (database , maxConnections , new HashMap <String , Object >() {{
83
+ put ("timeout" , timeout );
84
+ }});
85
+ }
86
+
87
+
88
+ //**************************************************************************
89
+ //** Constructor
90
+ //**************************************************************************
91
+ /** Used to instantiate the ConnectionPool using with a javaxt.sql.Database
92
+ * @param database javaxt.sql.Database with database connection information
93
+ * including a valid getConnectionPoolDataSource() response. The database
94
+ * object provides additional run-time query optimizations (e.g. connection
95
+ * metadata).
96
+ * @param maxConnections Maximum number of database connections for the
97
+ * connection pool.
98
+ * @param options Additional pool configuration options including:
99
+ * <ul>
100
+ * <li>timeout: The maximum time to wait for a free connection, in seconds. Default is 20 seconds.</li>
101
+ * <li>idleTimeout: Connection idle timeout in seconds. Default is 300 seconds (5 minutes).</li>
102
+ * <li>maxAge: Maximum connection age in seconds. Default is 1800 seconds (30 minutes).</li>
103
+ * <li>validationQuery: Query to validate connections. Default is "SELECT 1".</li>
104
+ * <li>validationTimeout: Interval used to execute validation queries. Default is 5 seconds.</li>
105
+ * </ul>
106
+ */
107
+ public ConnectionPool (Database database , int maxConnections , Map <String , Object > options ) throws SQLException {
108
+ if (database ==null ) throw new IllegalArgumentException ("Database is required" );
85
109
this .database = database ;
110
+ init (database .getConnectionPoolDataSource (), maxConnections , options );
86
111
}
87
112
88
113
89
114
//**************************************************************************
90
115
//** Constructor
91
116
//**************************************************************************
92
117
public ConnectionPool (ConnectionPoolDataSource dataSource , int maxConnections ) {
93
- this (dataSource , maxConnections , null );
118
+ init (dataSource , maxConnections , null );
94
119
}
95
120
96
121
97
122
//**************************************************************************
98
123
//** Constructor
99
124
//**************************************************************************
100
- /** Constructs a ConnectionPool object.
101
- * @param dataSource JDBC ConnectionPoolDataSource for the connections.
102
- * @param maxConnections The maximum number of connections.
103
- * @param timeout The maximum time in seconds to wait for a free connection.
104
- * Defaults to 20 seconds
105
- */
106
125
public ConnectionPool (ConnectionPoolDataSource dataSource , int maxConnections , Integer timeout ) {
107
- this (dataSource , maxConnections , timeout , null , null , null , null );
126
+ init (dataSource , maxConnections , new HashMap <String , Object >() {{
127
+ put ("timeout" , timeout );
128
+ }});
108
129
}
109
130
110
131
111
132
//**************************************************************************
112
133
//** Constructor
113
134
//**************************************************************************
114
- /** Constructs a ConnectionPool object with health monitoring configuration.
115
- * @param dataSource JDBC ConnectionPoolDataSource for the connections.
116
- * @param maxConnections The maximum number of connections.
117
- * @param timeout The maximum time to wait for a free connection, in seconds. Default is 20 seconds.
118
- * @param idleTimeout Connection idle timeout in seconds. Default is 300 seconds (5 minutes).
119
- * @param maxAge Maximum connection age in seconds. Default is 1800 seconds (30 minutes).
120
- * @param validationQuery Query to validate connections. Default is "SELECT 1".
121
- * @param validationTimeout
122
- */
123
- public ConnectionPool (ConnectionPoolDataSource dataSource , int maxConnections ,
124
- Integer timeout , Integer idleTimeout , Integer maxAge ,
125
- String validationQuery , Integer validationTimeout ) {
135
+ public ConnectionPool (ConnectionPoolDataSource dataSource , int maxConnections , Map <String , Object > options ) throws SQLException {
136
+ init (dataSource , maxConnections , options );
137
+ }
126
138
127
139
140
+ //**************************************************************************
141
+ //** init
142
+ //**************************************************************************
143
+ private void init (ConnectionPoolDataSource dataSource , int maxConnections , Map <String , Object > options ) {
144
+
128
145
if (dataSource ==null ) throw new IllegalArgumentException ("dataSource is required" );
129
146
if (maxConnections <1 ) throw new IllegalArgumentException ("Invalid maxConnections" );
130
- if (timeout ==null ) timeout = 20 ; //20 seconds default
131
- if (timeout <0 ) throw new IllegalArgumentException ("Invalid timeout: " + timeout );
147
+
148
+
149
+ if (options ==null ) options = new HashMap <>();
150
+ Integer timeout = new Value (options .get ("timeout" )).toInteger ();
151
+ if (timeout ==null || timeout <= 0 ) timeout = 20 ; // 20 seconds default
152
+
153
+ Integer idleTimeout = new Value (options .get ("idleTimeout" )).toInteger ();
132
154
if (idleTimeout ==null || idleTimeout <= 0 ) idleTimeout = 300 ; // 5 minutes default
155
+
156
+ Integer maxAge = new Value (options .get ("maxAge" )).toInteger ();
133
157
if (maxAge ==null || maxAge <= 0 ) maxAge = 1800 ; // 30 minutes default
134
- if (validationTimeout ==null || validationTimeout <= 0 ) validationTimeout = 5 ;
158
+
159
+ Integer validationTimeout = new Value (options .get ("validationTimeout" )).toInteger ();
160
+ if (validationTimeout ==null || validationTimeout <= 0 ) validationTimeout = 5 ; // 5 seconds
161
+
162
+ String validationQuery = new Value (options .get ("validationQuery" )).toString ();
135
163
if (validationQuery == null || validationQuery .trim ().isEmpty ()) {
136
164
validationQuery = "SELECT 1" ;
137
165
}
@@ -193,7 +221,6 @@ public boolean isClosed() {
193
221
}
194
222
195
223
196
-
197
224
//**************************************************************************
198
225
//** getConnection
199
226
//**************************************************************************
@@ -597,9 +624,17 @@ public int getTimeout(){
597
624
return Math .round (timeoutMs /1000 );
598
625
}
599
626
600
- /**
601
- * Returns the connection idle timeout in seconds.
602
- */
627
+
628
+ //**************************************************************************
629
+ //** getConnectionIdleTimeout
630
+ //**************************************************************************
631
+ /** Returns the connection idle timeout in seconds. Connections that remain
632
+ * unused in the pool for more than the idle timeout are automatically
633
+ * removed. This prevents accumulation of stale connections that may have
634
+ * been closed by the database server. Note that this only affects
635
+ * connections sitting idle in the pool, not active connections being used
636
+ * by your application.
637
+ */
603
638
public int getConnectionIdleTimeout () {
604
639
return Math .round (connectionIdleTimeoutMs /1000 );
605
640
}
@@ -946,7 +981,7 @@ private void log (String msg) {
946
981
String s = "ConnectionPool: " +msg ;
947
982
try {
948
983
if (logWriter == null ) {
949
- System .err .println (s );
984
+ // System.err.println(s);
950
985
}
951
986
else {
952
987
logWriter .println (s );
@@ -957,7 +992,6 @@ private void log (String msg) {
957
992
958
993
private void assertInnerState () {
959
994
int active = activeConnections .get ();
960
- int recycled = recycledConnections .size ();
961
995
int total = totalConnections .get ();
962
996
963
997
if (active < 0 ) {
@@ -1015,7 +1049,6 @@ PooledConnectionWrapper markUsed() {
1015
1049
}
1016
1050
1017
1051
1018
-
1019
1052
//**************************************************************************
1020
1053
//** getPoolStatistics
1021
1054
//**************************************************************************
@@ -1037,9 +1070,12 @@ public PoolStatistics getPoolStatistics() {
1037
1070
);
1038
1071
}
1039
1072
1040
- /**
1041
- * Pool statistics for monitoring connection pool health.
1042
- */
1073
+
1074
+ //**************************************************************************
1075
+ //** PoolStatistics Class
1076
+ //**************************************************************************
1077
+ /** Pool statistics for monitoring connection pool health.
1078
+ */
1043
1079
public static class PoolStatistics {
1044
1080
public final int activeConnections ;
1045
1081
public final int recycledConnections ;
0 commit comments