11package javaxt .sql ;
22
3+ import java .util .Map ;
4+ import java .util .HashMap ;
35import java .io .PrintWriter ;
46import java .sql .SQLException ;
57import javax .sql .ConnectionEvent ;
@@ -45,93 +47,119 @@ public class ConnectionPool {
4547 private final ConcurrentLinkedQueue <PooledConnectionWrapper > recycledConnections = new ConcurrentLinkedQueue <>();
4648 private final ConcurrentHashMap <PooledConnection , PooledConnectionWrapper > connectionWrappers = new ConcurrentHashMap <>();
4749 private volatile PooledConnection connectionInTransition ; // a PooledConnection which is currently within a PooledConnection.getConnection() call, or null
48- private Database database ;
4950
5051 // Validation caching for performance optimization
5152 private final ConcurrentHashMap <PooledConnection , Long > validationCache = new ConcurrentHashMap <>();
5253 private static final long VALIDATION_CACHE_TTL = 30000 ; // 30 seconds
5354
5455
56+ private Database database ;
57+
58+
5559 /**
56- * Thrown in {@link #getConnection()} or {@link #getValidConnection()} when no free connection becomes
60+ * Thrown in {@link #getConnection()} when no free connection becomes
5761 * available within <code>timeout</code> seconds.
5862 */
5963 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 ); }
6667 }
6768
6869
6970 //**************************************************************************
7071 //** Constructor
7172 //**************************************************************************
7273 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 );
7675 }
7776
7877
7978 //**************************************************************************
8079 //** Constructor
8180 //**************************************************************************
8281 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" );
85109 this .database = database ;
110+ init (database .getConnectionPoolDataSource (), maxConnections , options );
86111 }
87112
88113
89114 //**************************************************************************
90115 //** Constructor
91116 //**************************************************************************
92117 public ConnectionPool (ConnectionPoolDataSource dataSource , int maxConnections ) {
93- this (dataSource , maxConnections , null );
118+ init (dataSource , maxConnections , null );
94119 }
95120
96121
97122 //**************************************************************************
98123 //** Constructor
99124 //**************************************************************************
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- */
106125 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+ }});
108129 }
109130
110131
111132 //**************************************************************************
112133 //** Constructor
113134 //**************************************************************************
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+ }
126138
127139
140+ //**************************************************************************
141+ //** init
142+ //**************************************************************************
143+ private void init (ConnectionPoolDataSource dataSource , int maxConnections , Map <String , Object > options ) {
144+
128145 if (dataSource ==null ) throw new IllegalArgumentException ("dataSource is required" );
129146 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 ();
132154 if (idleTimeout ==null || idleTimeout <= 0 ) idleTimeout = 300 ; // 5 minutes default
155+
156+ Integer maxAge = new Value (options .get ("maxAge" )).toInteger ();
133157 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 ();
135163 if (validationQuery == null || validationQuery .trim ().isEmpty ()) {
136164 validationQuery = "SELECT 1" ;
137165 }
@@ -193,7 +221,6 @@ public boolean isClosed() {
193221 }
194222
195223
196-
197224 //**************************************************************************
198225 //** getConnection
199226 //**************************************************************************
@@ -597,9 +624,17 @@ public int getTimeout(){
597624 return Math .round (timeoutMs /1000 );
598625 }
599626
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+ */
603638 public int getConnectionIdleTimeout () {
604639 return Math .round (connectionIdleTimeoutMs /1000 );
605640 }
@@ -946,7 +981,7 @@ private void log (String msg) {
946981 String s = "ConnectionPool: " +msg ;
947982 try {
948983 if (logWriter == null ) {
949- System .err .println (s );
984+ // System.err.println(s);
950985 }
951986 else {
952987 logWriter .println (s );
@@ -957,7 +992,6 @@ private void log (String msg) {
957992
958993 private void assertInnerState () {
959994 int active = activeConnections .get ();
960- int recycled = recycledConnections .size ();
961995 int total = totalConnections .get ();
962996
963997 if (active < 0 ) {
@@ -1015,7 +1049,6 @@ PooledConnectionWrapper markUsed() {
10151049 }
10161050
10171051
1018-
10191052 //**************************************************************************
10201053 //** getPoolStatistics
10211054 //**************************************************************************
@@ -1037,9 +1070,12 @@ public PoolStatistics getPoolStatistics() {
10371070 );
10381071 }
10391072
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+ */
10431079 public static class PoolStatistics {
10441080 public final int activeConnections ;
10451081 public final int recycledConnections ;
0 commit comments